diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/layout/AppLayout.js | 8 | ||||
-rw-r--r-- | src/components/layout/Sidebar.js | 54 | ||||
-rw-r--r-- | src/components/services/content/ServiceDisabled.js | 48 | ||||
-rw-r--r-- | src/components/services/content/ServiceWebview.js | 59 | ||||
-rw-r--r-- | src/components/services/content/Services.js | 15 | ||||
-rw-r--r-- | src/components/services/content/WebviewCrashHandler.js | 81 | ||||
-rw-r--r-- | src/components/services/tabs/TabBarSortableList.js | 25 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 26 | ||||
-rw-r--r-- | src/components/services/tabs/Tabbar.js | 21 | ||||
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.js | 1 | ||||
-rw-r--r-- | src/components/settings/recipes/RecipesDashboard.js | 57 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 14 | ||||
-rw-r--r-- | src/components/settings/services/ServiceItem.js | 22 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 46 | ||||
-rw-r--r-- | src/components/ui/AppLoader.js | 2 | ||||
-rw-r--r-- | src/components/ui/InfoBar.js | 7 | ||||
-rw-r--r-- | src/components/ui/Loader.js | 5 | ||||
-rw-r--r-- | src/components/ui/Subscription.js | 19 |
18 files changed, 404 insertions, 106 deletions
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index f60c170a8..20dc2f764 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -23,6 +23,10 @@ const messages = defineMessages({ | |||
23 | id: 'infobar.buttonReloadServices', | 23 | id: 'infobar.buttonReloadServices', |
24 | defaultMessage: '!!!Reload services', | 24 | defaultMessage: '!!!Reload services', |
25 | }, | 25 | }, |
26 | changelog: { | ||
27 | id: 'infobar.buttonChangelog', | ||
28 | defaultMessage: '!!!Changelog', | ||
29 | }, | ||
26 | buttonInstallUpdate: { | 30 | buttonInstallUpdate: { |
27 | id: 'infobar.buttonInstallUpdate', | 31 | id: 'infobar.buttonInstallUpdate', |
28 | defaultMessage: '!!!Restart & install update', | 32 | defaultMessage: '!!!Restart & install update', |
@@ -135,7 +139,9 @@ export default class AppLayout extends Component { | |||
135 | sticky | 139 | sticky |
136 | > | 140 | > |
137 | <span className="mdi mdi-information" /> | 141 | <span className="mdi mdi-information" /> |
138 | {intl.formatMessage(messages.updateAvailable)} | 142 | {intl.formatMessage(messages.updateAvailable)} <a href="https://meetfranz.com/changelog" target="_blank"> |
143 | <u>{intl.formatMessage(messages.changelog)}</u> | ||
144 | </a> | ||
139 | </InfoBar> | 145 | </InfoBar> |
140 | )} | 146 | )} |
141 | {services} | 147 | {services} |
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 6a5c0f365..cb2ecc8ce 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js | |||
@@ -2,6 +2,7 @@ import React, { Component } from 'react'; | |||
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import ReactTooltip from 'react-tooltip'; | 3 | import ReactTooltip from 'react-tooltip'; |
4 | import { defineMessages, intlShape } from 'react-intl'; | 4 | import { defineMessages, intlShape } from 'react-intl'; |
5 | import { observer } from 'mobx-react'; | ||
5 | 6 | ||
6 | import Tabbar from '../services/tabs/Tabbar'; | 7 | import Tabbar from '../services/tabs/Tabbar'; |
7 | import { ctrlKey } from '../../environment'; | 8 | import { ctrlKey } from '../../environment'; |
@@ -11,16 +12,26 @@ const messages = defineMessages({ | |||
11 | id: 'sidebar.settings', | 12 | id: 'sidebar.settings', |
12 | defaultMessage: '!!!Settings', | 13 | defaultMessage: '!!!Settings', |
13 | }, | 14 | }, |
15 | addNewService: { | ||
16 | id: 'sidebar.addNewService', | ||
17 | defaultMessage: '!!!Add new service', | ||
18 | }, | ||
19 | mute: { | ||
20 | id: 'sidebar.mute', | ||
21 | defaultMessage: '!!!Disable audio', | ||
22 | }, | ||
23 | unmute: { | ||
24 | id: 'sidebar.unmute', | ||
25 | defaultMessage: '!!!Enable audio', | ||
26 | }, | ||
14 | }); | 27 | }); |
15 | 28 | ||
29 | @observer | ||
16 | export default class Sidebar extends Component { | 30 | export default class Sidebar extends Component { |
17 | static propTypes = { | 31 | static propTypes = { |
18 | openSettings: PropTypes.func.isRequired, | 32 | openSettings: PropTypes.func.isRequired, |
19 | isPremiumUser: PropTypes.bool, | 33 | toggleMuteApp: PropTypes.func.isRequired, |
20 | } | 34 | isAppMuted: PropTypes.bool.isRequired, |
21 | |||
22 | static defaultProps = { | ||
23 | isPremiumUser: false, | ||
24 | } | 35 | } |
25 | 36 | ||
26 | static contextTypes = { | 37 | static contextTypes = { |
@@ -40,8 +51,9 @@ export default class Sidebar extends Component { | |||
40 | } | 51 | } |
41 | 52 | ||
42 | render() { | 53 | render() { |
43 | const { openSettings, isPremiumUser } = this.props; | 54 | const { openSettings, toggleMuteApp, isAppMuted } = this.props; |
44 | const { intl } = this.context; | 55 | const { intl } = this.context; |
56 | |||
45 | return ( | 57 | return ( |
46 | <div className="sidebar"> | 58 | <div className="sidebar"> |
47 | <Tabbar | 59 | <Tabbar |
@@ -50,21 +62,25 @@ export default class Sidebar extends Component { | |||
50 | disableToolTip={() => this.disableToolTip()} | 62 | disableToolTip={() => this.disableToolTip()} |
51 | /> | 63 | /> |
52 | <button | 64 | <button |
53 | onClick={openSettings} | 65 | onClick={toggleMuteApp} |
54 | className="sidebar__settings-button" | 66 | className={`sidebar__button sidebar__button--audio ${isAppMuted ? 'is-muted' : ''}`} |
67 | data-tip={`${intl.formatMessage(isAppMuted ? messages.unmute : messages.mute)} (${ctrlKey}+Shift+M)`} | ||
68 | > | ||
69 | <i className={`mdi mdi-bell${isAppMuted ? '-off' : ''}`} /> | ||
70 | </button> | ||
71 | <button | ||
72 | onClick={() => openSettings({ path: 'recipes' })} | ||
73 | className="sidebar__button sidebar__button--new-service" | ||
74 | data-tip={`${intl.formatMessage(messages.addNewService)} (${ctrlKey}+N)`} | ||
75 | > | ||
76 | <i className="mdi mdi-plus-box" /> | ||
77 | </button> | ||
78 | <button | ||
79 | onClick={() => openSettings({ path: 'app' })} | ||
80 | className="sidebar__button sidebar__button--settings" | ||
55 | data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} | 81 | data-tip={`${intl.formatMessage(messages.settings)} (${ctrlKey}+,)`} |
56 | > | 82 | > |
57 | {isPremiumUser && ( | 83 | <i className="mdi mdi-settings" /> |
58 | <span className="emoji"> | ||
59 | <img src="./assets/images/emoji/star.png" alt="" /> | ||
60 | </span> | ||
61 | )} | ||
62 | <img | ||
63 | src="./assets/images/logo.svg" | ||
64 | className="sidebar__logo" | ||
65 | alt="" | ||
66 | /> | ||
67 | {intl.formatMessage(messages.settings)} | ||
68 | </button> | 84 | </button> |
69 | {this.state.tooltipEnabled && ( | 85 | {this.state.tooltipEnabled && ( |
70 | <ReactTooltip place="right" type="dark" effect="solid" /> | 86 | <ReactTooltip place="right" type="dark" effect="solid" /> |
diff --git a/src/components/services/content/ServiceDisabled.js b/src/components/services/content/ServiceDisabled.js new file mode 100644 index 000000000..b5af3743d --- /dev/null +++ b/src/components/services/content/ServiceDisabled.js | |||
@@ -0,0 +1,48 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import Button from '../../ui/Button'; | ||
7 | |||
8 | const messages = defineMessages({ | ||
9 | headline: { | ||
10 | id: 'service.disabledHandler.headline', | ||
11 | defaultMessage: '!!!{name} is disabled', | ||
12 | }, | ||
13 | action: { | ||
14 | id: 'service.disabledHandler.action', | ||
15 | defaultMessage: '!!!Enable {name}', | ||
16 | }, | ||
17 | }); | ||
18 | |||
19 | @observer | ||
20 | export default class ServiceDisabled extends Component { | ||
21 | static propTypes = { | ||
22 | name: PropTypes.string.isRequired, | ||
23 | enable: PropTypes.func.isRequired, | ||
24 | }; | ||
25 | |||
26 | static contextTypes = { | ||
27 | intl: intlShape, | ||
28 | }; | ||
29 | |||
30 | countdownInterval = null; | ||
31 | countdownIntervalTimeout = 1000; | ||
32 | |||
33 | render() { | ||
34 | const { name, enable } = this.props; | ||
35 | const { intl } = this.context; | ||
36 | |||
37 | return ( | ||
38 | <div className="services__info-layer"> | ||
39 | <h1>{intl.formatMessage(messages.headline, { name })}</h1> | ||
40 | <Button | ||
41 | label={intl.formatMessage(messages.action, { name })} | ||
42 | buttonType="inverted" | ||
43 | onClick={() => enable()} | ||
44 | /> | ||
45 | </div> | ||
46 | ); | ||
47 | } | ||
48 | } | ||
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index 3ee3155be..faa356d3d 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js | |||
@@ -7,12 +7,17 @@ import classnames from 'classnames'; | |||
7 | 7 | ||
8 | import ServiceModel from '../../../models/Service'; | 8 | import ServiceModel from '../../../models/Service'; |
9 | import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl'; | 9 | import StatusBarTargetUrl from '../../ui/StatusBarTargetUrl'; |
10 | import WebviewCrashHandler from './WebviewCrashHandler'; | ||
11 | import ServiceDisabled from './ServiceDisabled'; | ||
10 | 12 | ||
11 | @observer | 13 | @observer |
12 | export default class ServiceWebview extends Component { | 14 | export default class ServiceWebview extends Component { |
13 | static propTypes = { | 15 | static propTypes = { |
14 | service: PropTypes.instanceOf(ServiceModel).isRequired, | 16 | service: PropTypes.instanceOf(ServiceModel).isRequired, |
15 | setWebviewReference: PropTypes.func.isRequired, | 17 | setWebviewReference: PropTypes.func.isRequired, |
18 | reload: PropTypes.func.isRequired, | ||
19 | isAppMuted: PropTypes.bool.isRequired, | ||
20 | enable: PropTypes.func.isRequired, | ||
16 | }; | 21 | }; |
17 | 22 | ||
18 | static defaultProps = { | 23 | static defaultProps = { |
@@ -53,6 +58,9 @@ export default class ServiceWebview extends Component { | |||
53 | const { | 58 | const { |
54 | service, | 59 | service, |
55 | setWebviewReference, | 60 | setWebviewReference, |
61 | reload, | ||
62 | isAppMuted, | ||
63 | enable, | ||
56 | } = this.props; | 64 | } = this.props; |
57 | 65 | ||
58 | const webviewClasses = classnames({ | 66 | const webviewClasses = classnames({ |
@@ -70,26 +78,37 @@ export default class ServiceWebview extends Component { | |||
70 | 78 | ||
71 | return ( | 79 | return ( |
72 | <div className={webviewClasses}> | 80 | <div className={webviewClasses}> |
73 | <Webview | 81 | {service.hasCrashed && ( |
74 | ref={(element) => { this.webview = element; }} | 82 | <WebviewCrashHandler |
75 | 83 | name={service.recipe.name} | |
76 | autosize | 84 | webview={service.webview} |
77 | src={service.url} | 85 | reload={reload} |
78 | preload="./webview/plugin.js" | 86 | /> |
79 | partition={`persist:service-${service.id}`} | 87 | )} |
80 | 88 | {!service.isEnabled ? ( | |
81 | onDidAttach={() => setWebviewReference({ | 89 | <ServiceDisabled |
82 | serviceId: service.id, | 90 | name={service.recipe.name} |
83 | webview: this.webview.view, | 91 | webview={service.webview} |
84 | })} | 92 | enable={enable} |
85 | 93 | /> | |
86 | onUpdateTargetUrl={this.updateTargetUrl} | 94 | ) : ( |
87 | 95 | <Webview | |
88 | useragent={service.userAgent} | 96 | ref={(element) => { this.webview = element; }} |
89 | 97 | autosize | |
90 | disablewebsecurity | 98 | src={service.url} |
91 | allowpopups | 99 | preload="./webview/plugin.js" |
92 | /> | 100 | partition={`persist:service-${service.id}`} |
101 | onDidAttach={() => setWebviewReference({ | ||
102 | serviceId: service.id, | ||
103 | webview: this.webview.view, | ||
104 | })} | ||
105 | onUpdateTargetUrl={this.updateTargetUrl} | ||
106 | useragent={service.userAgent} | ||
107 | muted={isAppMuted || service.isMuted} | ||
108 | disablewebsecurity | ||
109 | allowpopups | ||
110 | /> | ||
111 | )} | ||
93 | {statusBar} | 112 | {statusBar} |
94 | </div> | 113 | </div> |
95 | ); | 114 | ); |
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index 03c68b06f..b1322afc2 100644 --- a/src/components/services/content/Services.js +++ b/src/components/services/content/Services.js | |||
@@ -25,6 +25,9 @@ export default class Services extends Component { | |||
25 | setWebviewReference: PropTypes.func.isRequired, | 25 | setWebviewReference: PropTypes.func.isRequired, |
26 | handleIPCMessage: PropTypes.func.isRequired, | 26 | handleIPCMessage: PropTypes.func.isRequired, |
27 | openWindow: PropTypes.func.isRequired, | 27 | openWindow: PropTypes.func.isRequired, |
28 | reload: PropTypes.func.isRequired, | ||
29 | isAppMuted: PropTypes.bool.isRequired, | ||
30 | update: PropTypes.func.isRequired, | ||
28 | }; | 31 | }; |
29 | 32 | ||
30 | static defaultProps = { | 33 | static defaultProps = { |
@@ -42,6 +45,9 @@ export default class Services extends Component { | |||
42 | handleIPCMessage, | 45 | handleIPCMessage, |
43 | setWebviewReference, | 46 | setWebviewReference, |
44 | openWindow, | 47 | openWindow, |
48 | reload, | ||
49 | isAppMuted, | ||
50 | update, | ||
45 | } = this.props; | 51 | } = this.props; |
46 | const { intl } = this.context; | 52 | const { intl } = this.context; |
47 | 53 | ||
@@ -73,6 +79,15 @@ export default class Services extends Component { | |||
73 | handleIPCMessage={handleIPCMessage} | 79 | handleIPCMessage={handleIPCMessage} |
74 | setWebviewReference={setWebviewReference} | 80 | setWebviewReference={setWebviewReference} |
75 | openWindow={openWindow} | 81 | openWindow={openWindow} |
82 | reload={() => reload({ serviceId: service.id })} | ||
83 | isAppMuted={isAppMuted} | ||
84 | enable={() => update({ | ||
85 | serviceId: service.id, | ||
86 | serviceData: { | ||
87 | isEnabled: true, | ||
88 | }, | ||
89 | redirect: false, | ||
90 | })} | ||
76 | /> | 91 | /> |
77 | ))} | 92 | ))} |
78 | </div> | 93 | </div> |
diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js new file mode 100644 index 000000000..d3e6951f3 --- /dev/null +++ b/src/components/services/content/WebviewCrashHandler.js | |||
@@ -0,0 +1,81 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import { defineMessages, intlShape } from 'react-intl'; | ||
5 | |||
6 | import Button from '../../ui/Button'; | ||
7 | |||
8 | const messages = defineMessages({ | ||
9 | headline: { | ||
10 | id: 'service.crashHandler.headline', | ||
11 | defaultMessage: '!!!Oh no!', | ||
12 | }, | ||
13 | text: { | ||
14 | id: 'service.crashHandler.text', | ||
15 | defaultMessage: '!!!{name} has caused an error.', | ||
16 | }, | ||
17 | action: { | ||
18 | id: 'service.crashHandler.action', | ||
19 | defaultMessage: '!!!Reload {name}', | ||
20 | }, | ||
21 | autoReload: { | ||
22 | id: 'service.crashHandler.autoReload', | ||
23 | defaultMessage: '!!!Trying to automatically restore {name} in {seconds} seconds', | ||
24 | }, | ||
25 | }); | ||
26 | |||
27 | @observer | ||
28 | export default class WebviewCrashHandler extends Component { | ||
29 | static propTypes = { | ||
30 | name: PropTypes.string.isRequired, | ||
31 | reload: PropTypes.func.isRequired, | ||
32 | }; | ||
33 | |||
34 | static contextTypes = { | ||
35 | intl: intlShape, | ||
36 | }; | ||
37 | |||
38 | state = { | ||
39 | countdown: 10000, | ||
40 | } | ||
41 | |||
42 | componentDidMount() { | ||
43 | const { reload } = this.props; | ||
44 | |||
45 | this.countdownInterval = setInterval(() => { | ||
46 | this.setState({ | ||
47 | countdown: this.state.countdown - this.countdownIntervalTimeout, | ||
48 | }); | ||
49 | |||
50 | if (this.state.countdown <= 0) { | ||
51 | reload(); | ||
52 | clearInterval(this.countdownInterval); | ||
53 | } | ||
54 | }, this.countdownIntervalTimeout); | ||
55 | } | ||
56 | |||
57 | countdownInterval = null; | ||
58 | countdownIntervalTimeout = 1000; | ||
59 | |||
60 | render() { | ||
61 | const { name, reload } = this.props; | ||
62 | const { intl } = this.context; | ||
63 | |||
64 | return ( | ||
65 | <div className="services__info-layer"> | ||
66 | <h1>{intl.formatMessage(messages.headline)}</h1> | ||
67 | <p>{intl.formatMessage(messages.text, { name })}</p> | ||
68 | <Button | ||
69 | // label={`Reload ${name}`} | ||
70 | label={intl.formatMessage(messages.action, { name })} | ||
71 | buttonType="inverted" | ||
72 | onClick={() => reload()} | ||
73 | /> | ||
74 | <p className="footnote">{intl.formatMessage(messages.autoReload, { | ||
75 | name, | ||
76 | seconds: this.state.countdown / 1000, | ||
77 | })}</p> | ||
78 | </div> | ||
79 | ); | ||
80 | } | ||
81 | } | ||
diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js index e5ae36419..2daf55676 100644 --- a/src/components/services/tabs/TabBarSortableList.js +++ b/src/components/services/tabs/TabBarSortableList.js | |||
@@ -2,17 +2,8 @@ import React, { Component } from 'react'; | |||
2 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 2 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { SortableContainer } from 'react-sortable-hoc'; | 4 | import { SortableContainer } from 'react-sortable-hoc'; |
5 | import { defineMessages, intlShape } from 'react-intl'; | ||
6 | 5 | ||
7 | import TabItem from './TabItem'; | 6 | import TabItem from './TabItem'; |
8 | import { ctrlKey } from '../../../environment'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | addNewService: { | ||
12 | id: 'sidebar.addNewService', | ||
13 | defaultMessage: '!!!Add new service', | ||
14 | }, | ||
15 | }); | ||
16 | 7 | ||
17 | @observer | 8 | @observer |
18 | class TabBarSortableList extends Component { | 9 | class TabBarSortableList extends Component { |
@@ -22,27 +13,25 @@ class TabBarSortableList extends Component { | |||
22 | openSettings: PropTypes.func.isRequired, | 13 | openSettings: PropTypes.func.isRequired, |
23 | reload: PropTypes.func.isRequired, | 14 | reload: PropTypes.func.isRequired, |
24 | toggleNotifications: PropTypes.func.isRequired, | 15 | toggleNotifications: PropTypes.func.isRequired, |
16 | toggleAudio: PropTypes.func.isRequired, | ||
25 | deleteService: PropTypes.func.isRequired, | 17 | deleteService: PropTypes.func.isRequired, |
26 | disableService: PropTypes.func.isRequired, | 18 | disableService: PropTypes.func.isRequired, |
19 | enableService: PropTypes.func.isRequired, | ||
27 | } | 20 | } |
28 | 21 | ||
29 | static contextTypes = { | ||
30 | intl: intlShape, | ||
31 | }; | ||
32 | |||
33 | render() { | 22 | render() { |
34 | const { | 23 | const { |
35 | services, | 24 | services, |
36 | setActive, | 25 | setActive, |
37 | reload, | 26 | reload, |
38 | toggleNotifications, | 27 | toggleNotifications, |
28 | toggleAudio, | ||
39 | deleteService, | 29 | deleteService, |
40 | disableService, | 30 | disableService, |
31 | enableService, | ||
41 | openSettings, | 32 | openSettings, |
42 | } = this.props; | 33 | } = this.props; |
43 | 34 | ||
44 | const { intl } = this.context; | ||
45 | |||
46 | return ( | 35 | return ( |
47 | <ul | 36 | <ul |
48 | className="tabs" | 37 | className="tabs" |
@@ -56,12 +45,14 @@ class TabBarSortableList extends Component { | |||
56 | shortcutIndex={index + 1} | 45 | shortcutIndex={index + 1} |
57 | reload={() => reload({ serviceId: service.id })} | 46 | reload={() => reload({ serviceId: service.id })} |
58 | toggleNotifications={() => toggleNotifications({ serviceId: service.id })} | 47 | toggleNotifications={() => toggleNotifications({ serviceId: service.id })} |
48 | toggleAudio={() => toggleAudio({ serviceId: service.id })} | ||
59 | deleteService={() => deleteService({ serviceId: service.id })} | 49 | deleteService={() => deleteService({ serviceId: service.id })} |
60 | disableService={() => disableService({ serviceId: service.id })} | 50 | disableService={() => disableService({ serviceId: service.id })} |
51 | enableService={() => enableService({ serviceId: service.id })} | ||
61 | openSettings={openSettings} | 52 | openSettings={openSettings} |
62 | /> | 53 | /> |
63 | ))} | 54 | ))} |
64 | <li> | 55 | {/* <li> |
65 | <button | 56 | <button |
66 | className="sidebar__add-service" | 57 | className="sidebar__add-service" |
67 | onClick={() => openSettings({ path: 'recipes' })} | 58 | onClick={() => openSettings({ path: 'recipes' })} |
@@ -69,7 +60,7 @@ class TabBarSortableList extends Component { | |||
69 | > | 60 | > |
70 | <span className="mdi mdi-plus" /> | 61 | <span className="mdi mdi-plus" /> |
71 | </button> | 62 | </button> |
72 | </li> | 63 | </li> */} |
73 | </ul> | 64 | </ul> |
74 | ); | 65 | ); |
75 | } | 66 | } |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 9e03d2e21..a7136c43f 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -28,10 +28,22 @@ const messages = defineMessages({ | |||
28 | id: 'tabs.item.enableNotification', | 28 | id: 'tabs.item.enableNotification', |
29 | defaultMessage: '!!!Enable notifications', | 29 | defaultMessage: '!!!Enable notifications', |
30 | }, | 30 | }, |
31 | disableAudio: { | ||
32 | id: 'tabs.item.disableAudio', | ||
33 | defaultMessage: '!!!Disable audio', | ||
34 | }, | ||
35 | enableAudio: { | ||
36 | id: 'tabs.item.enableAudio', | ||
37 | defaultMessage: '!!!Enable audio', | ||
38 | }, | ||
31 | disableService: { | 39 | disableService: { |
32 | id: 'tabs.item.disableService', | 40 | id: 'tabs.item.disableService', |
33 | defaultMessage: '!!!Disable Service', | 41 | defaultMessage: '!!!Disable Service', |
34 | }, | 42 | }, |
43 | enableService: { | ||
44 | id: 'tabs.item.enableService', | ||
45 | defaultMessage: '!!!Enable Service', | ||
46 | }, | ||
35 | deleteService: { | 47 | deleteService: { |
36 | id: 'tabs.item.deleteService', | 48 | id: 'tabs.item.deleteService', |
37 | defaultMessage: '!!!Delete Service', | 49 | defaultMessage: '!!!Delete Service', |
@@ -46,9 +58,11 @@ class TabItem extends Component { | |||
46 | shortcutIndex: PropTypes.number.isRequired, | 58 | shortcutIndex: PropTypes.number.isRequired, |
47 | reload: PropTypes.func.isRequired, | 59 | reload: PropTypes.func.isRequired, |
48 | toggleNotifications: PropTypes.func.isRequired, | 60 | toggleNotifications: PropTypes.func.isRequired, |
61 | toggleAudio: PropTypes.func.isRequired, | ||
49 | openSettings: PropTypes.func.isRequired, | 62 | openSettings: PropTypes.func.isRequired, |
50 | deleteService: PropTypes.func.isRequired, | 63 | deleteService: PropTypes.func.isRequired, |
51 | disableService: PropTypes.func.isRequired, | 64 | disableService: PropTypes.func.isRequired, |
65 | enableService: PropTypes.func.isRequired, | ||
52 | }; | 66 | }; |
53 | 67 | ||
54 | static contextTypes = { | 68 | static contextTypes = { |
@@ -62,8 +76,10 @@ class TabItem extends Component { | |||
62 | shortcutIndex, | 76 | shortcutIndex, |
63 | reload, | 77 | reload, |
64 | toggleNotifications, | 78 | toggleNotifications, |
79 | toggleAudio, | ||
65 | deleteService, | 80 | deleteService, |
66 | disableService, | 81 | disableService, |
82 | enableService, | ||
67 | openSettings, | 83 | openSettings, |
68 | } = this.props; | 84 | } = this.props; |
69 | const { intl } = this.context; | 85 | const { intl } = this.context; |
@@ -90,8 +106,13 @@ class TabItem extends Component { | |||
90 | : intl.formatMessage(messages.enableNotifications), | 106 | : intl.formatMessage(messages.enableNotifications), |
91 | click: () => toggleNotifications(), | 107 | click: () => toggleNotifications(), |
92 | }, { | 108 | }, { |
93 | label: intl.formatMessage(messages.disableService), | 109 | label: service.isMuted |
94 | click: () => disableService(), | 110 | ? intl.formatMessage(messages.enableAudio) |
111 | : intl.formatMessage(messages.disableAudio), | ||
112 | click: () => toggleAudio(), | ||
113 | }, { | ||
114 | label: intl.formatMessage(service.isEnabled ? messages.disableService : messages.enableService), | ||
115 | click: () => (service.isEnabled ? disableService() : enableService()), | ||
95 | }, { | 116 | }, { |
96 | type: 'separator', | 117 | type: 'separator', |
97 | }, { | 118 | }, { |
@@ -106,6 +127,7 @@ class TabItem extends Component { | |||
106 | 'tab-item': true, | 127 | 'tab-item': true, |
107 | 'is-active': service.isActive, | 128 | 'is-active': service.isActive, |
108 | 'has-custom-icon': service.hasCustomIcon, | 129 | 'has-custom-icon': service.hasCustomIcon, |
130 | 'is-disabled': !service.isEnabled, | ||
109 | })} | 131 | })} |
110 | onClick={clickHandler} | 132 | onClick={clickHandler} |
111 | onContextMenu={() => menu.popup(remote.getCurrentWindow())} | 133 | onContextMenu={() => menu.popup(remote.getCurrentWindow())} |
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index fdb2c0a59..9da1090b7 100644 --- a/src/components/services/tabs/Tabbar.js +++ b/src/components/services/tabs/Tabbar.js | |||
@@ -15,6 +15,7 @@ export default class TabBar extends Component { | |||
15 | reorder: PropTypes.func.isRequired, | 15 | reorder: PropTypes.func.isRequired, |
16 | reload: PropTypes.func.isRequired, | 16 | reload: PropTypes.func.isRequired, |
17 | toggleNotifications: PropTypes.func.isRequired, | 17 | toggleNotifications: PropTypes.func.isRequired, |
18 | toggleAudio: PropTypes.func.isRequired, | ||
18 | deleteService: PropTypes.func.isRequired, | 19 | deleteService: PropTypes.func.isRequired, |
19 | updateService: PropTypes.func.isRequired, | 20 | updateService: PropTypes.func.isRequired, |
20 | } | 21 | } |
@@ -29,20 +30,30 @@ export default class TabBar extends Component { | |||
29 | reorder({ oldIndex, newIndex }); | 30 | reorder({ oldIndex, newIndex }); |
30 | }; | 31 | }; |
31 | 32 | ||
32 | disableService = ({ serviceId }) => { | 33 | shouldPreventSorting = event => event.target.tagName !== 'LI'; |
34 | |||
35 | toggleService = ({ serviceId, isEnabled }) => { | ||
33 | const { updateService } = this.props; | 36 | const { updateService } = this.props; |
34 | 37 | ||
35 | if (serviceId) { | 38 | if (serviceId) { |
36 | updateService({ | 39 | updateService({ |
37 | serviceId, | 40 | serviceId, |
38 | serviceData: { | 41 | serviceData: { |
39 | isEnabled: false, | 42 | isEnabled, |
40 | }, | 43 | }, |
41 | redirect: false, | 44 | redirect: false, |
42 | }); | 45 | }); |
43 | } | 46 | } |
44 | } | 47 | } |
45 | 48 | ||
49 | disableService({ serviceId }) { | ||
50 | this.toggleService({ serviceId, isEnabled: false }); | ||
51 | } | ||
52 | |||
53 | enableService({ serviceId }) { | ||
54 | this.toggleService({ serviceId, isEnabled: true }); | ||
55 | } | ||
56 | |||
46 | render() { | 57 | render() { |
47 | const { | 58 | const { |
48 | services, | 59 | services, |
@@ -51,6 +62,7 @@ export default class TabBar extends Component { | |||
51 | disableToolTip, | 62 | disableToolTip, |
52 | reload, | 63 | reload, |
53 | toggleNotifications, | 64 | toggleNotifications, |
65 | toggleAudio, | ||
54 | deleteService, | 66 | deleteService, |
55 | } = this.props; | 67 | } = this.props; |
56 | 68 | ||
@@ -61,10 +73,13 @@ export default class TabBar extends Component { | |||
61 | setActive={setActive} | 73 | setActive={setActive} |
62 | onSortEnd={this.onSortEnd} | 74 | onSortEnd={this.onSortEnd} |
63 | onSortStart={disableToolTip} | 75 | onSortStart={disableToolTip} |
76 | shouldCancelStart={this.shouldPreventSorting} | ||
64 | reload={reload} | 77 | reload={reload} |
65 | toggleNotifications={toggleNotifications} | 78 | toggleNotifications={toggleNotifications} |
79 | toggleAudio={toggleAudio} | ||
66 | deleteService={deleteService} | 80 | deleteService={deleteService} |
67 | disableService={this.disableService} | 81 | disableService={args => this.disableService(args)} |
82 | enableService={args => this.enableService(args)} | ||
68 | openSettings={openSettings} | 83 | openSettings={openSettings} |
69 | distance={20} | 84 | distance={20} |
70 | axis="y" | 85 | axis="y" |
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 3b21a7765..fea8d682d 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -74,7 +74,6 @@ export default class SettingsNavigation extends Component { | |||
74 | <Link | 74 | <Link |
75 | to="/auth/logout" | 75 | to="/auth/logout" |
76 | className="settings-navigation__link" | 76 | className="settings-navigation__link" |
77 | activeClassName="is-active" | ||
78 | > | 77 | > |
79 | {intl.formatMessage(messages.logout)} | 78 | {intl.formatMessage(messages.logout)} |
80 | </Link> | 79 | </Link> |
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index 02ea04e35..b6ade5da4 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -9,6 +9,7 @@ import Infobox from '../../ui/Infobox'; | |||
9 | import RecipeItem from './RecipeItem'; | 9 | import RecipeItem from './RecipeItem'; |
10 | import Loader from '../../ui/Loader'; | 10 | import Loader from '../../ui/Loader'; |
11 | import Appear from '../../ui/effects/Appear'; | 11 | import Appear from '../../ui/effects/Appear'; |
12 | import { FRANZ_SERVICE_REQUEST } from '../../../config'; | ||
12 | 13 | ||
13 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
14 | headline: { | 15 | headline: { |
@@ -35,6 +36,10 @@ const messages = defineMessages({ | |||
35 | id: 'settings.recipes.servicesSuccessfulAddedInfo', | 36 | id: 'settings.recipes.servicesSuccessfulAddedInfo', |
36 | defaultMessage: '!!!Service successfully added', | 37 | defaultMessage: '!!!Service successfully added', |
37 | }, | 38 | }, |
39 | missingService: { | ||
40 | id: 'settings.recipes.missingService', | ||
41 | defaultMessage: '!!!Missing a service?', | ||
42 | }, | ||
38 | }); | 43 | }); |
39 | 44 | ||
40 | @observer | 45 | @observer |
@@ -96,33 +101,39 @@ export default class RecipesDashboard extends Component { | |||
96 | </Infobox> | 101 | </Infobox> |
97 | </Appear> | 102 | </Appear> |
98 | )} | 103 | )} |
99 | {!searchNeedle && ( | 104 | {/* {!searchNeedle && ( */} |
100 | <div className="recipes__navigation"> | 105 | <div className="recipes__navigation"> |
101 | <Link | 106 | <Link |
102 | to="/settings/recipes" | 107 | to="/settings/recipes" |
103 | className="badge" | 108 | className="badge" |
104 | activeClassName="badge--primary" | 109 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} |
105 | > | 110 | onClick={() => resetSearch()} |
106 | {intl.formatMessage(messages.mostPopularRecipes)} | 111 | > |
107 | </Link> | 112 | {intl.formatMessage(messages.mostPopularRecipes)} |
113 | </Link> | ||
114 | <Link | ||
115 | to="/settings/recipes/all" | ||
116 | className="badge" | ||
117 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} | ||
118 | onClick={() => resetSearch()} | ||
119 | > | ||
120 | {intl.formatMessage(messages.allRecipes)} | ||
121 | </Link> | ||
122 | {devRecipesCount > 0 && ( | ||
108 | <Link | 123 | <Link |
109 | to="/settings/recipes/all" | 124 | to="/settings/recipes/dev" |
110 | className="badge" | 125 | className="badge" |
111 | activeClassName="badge--primary" | 126 | activeClassName={`${!searchNeedle ? 'badge--primary' : ''}`} |
127 | onClick={() => resetSearch()} | ||
112 | > | 128 | > |
113 | {intl.formatMessage(messages.allRecipes)} | 129 | {intl.formatMessage(messages.devRecipes)} ({devRecipesCount}) |
114 | </Link> | 130 | </Link> |
115 | {devRecipesCount > 0 && ( | 131 | )} |
116 | <Link | 132 | <a href={FRANZ_SERVICE_REQUEST} target="_blank" className="link recipes__service-request"> |
117 | to="/settings/recipes/dev" | 133 | {intl.formatMessage(messages.missingService)} <i className="mdi mdi-open-in-new" /> |
118 | className="badge" | 134 | </a> |
119 | activeClassName="badge--primary" | 135 | </div> |
120 | > | 136 | {/* )} */} |
121 | {intl.formatMessage(messages.devRecipes)} ({devRecipesCount}) | ||
122 | </Link> | ||
123 | )} | ||
124 | </div> | ||
125 | )} | ||
126 | {isLoading ? ( | 137 | {isLoading ? ( |
127 | <Loader /> | 138 | <Loader /> |
128 | ) : ( | 139 | ) : ( |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 9b359a78e..36cefe87c 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -61,7 +61,11 @@ const messages = defineMessages({ | |||
61 | }, | 61 | }, |
62 | indirectMessageInfo: { | 62 | indirectMessageInfo: { |
63 | id: 'settings.service.form.indirectMessageInfo', | 63 | id: 'settings.service.form.indirectMessageInfo', |
64 | defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...', // eslint-disable-line | 64 | defaultMessage: '!!!You will be notified about all new messages in a channel, not just @username, @channel, @here, ...', |
65 | }, | ||
66 | isMutedInfo: { | ||
67 | id: 'settings.service.form.isMutedInfo', | ||
68 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', | ||
65 | }, | 69 | }, |
66 | }); | 70 | }); |
67 | 71 | ||
@@ -110,7 +114,7 @@ export default class EditServiceForm extends Component { | |||
110 | if (recipe.validateUrl && values.customUrl) { | 114 | if (recipe.validateUrl && values.customUrl) { |
111 | this.setState({ isValidatingCustomUrl: true }); | 115 | this.setState({ isValidatingCustomUrl: true }); |
112 | try { | 116 | try { |
113 | values.customUrl = normalizeUrl(values.customUrl); | 117 | values.customUrl = normalizeUrl(values.customUrl, { stripWWW: false }); |
114 | isValid = await recipe.validateUrl(values.customUrl); | 118 | isValid = await recipe.validateUrl(values.customUrl); |
115 | } catch (err) { | 119 | } catch (err) { |
116 | console.warn('ValidateURL', err); | 120 | console.warn('ValidateURL', err); |
@@ -231,11 +235,15 @@ export default class EditServiceForm extends Component { | |||
231 | {recipe.hasIndirectMessages && ( | 235 | {recipe.hasIndirectMessages && ( |
232 | <div> | 236 | <div> |
233 | <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> | 237 | <Toggle field={form.$('isIndirectMessageBadgeEnabled')} /> |
234 | <p className="settings__indirect-message-help"> | 238 | <p className="settings__help"> |
235 | {intl.formatMessage(messages.indirectMessageInfo)} | 239 | {intl.formatMessage(messages.indirectMessageInfo)} |
236 | </p> | 240 | </p> |
237 | </div> | 241 | </div> |
238 | )} | 242 | )} |
243 | <Toggle field={form.$('isMuted')} /> | ||
244 | <p className="settings__help"> | ||
245 | {intl.formatMessage(messages.isMutedInfo)} | ||
246 | </p> | ||
239 | <Toggle field={form.$('isEnabled')} /> | 247 | <Toggle field={form.$('isEnabled')} /> |
240 | </div> | 248 | </div> |
241 | {recipe.message && ( | 249 | {recipe.message && ( |
diff --git a/src/components/settings/services/ServiceItem.js b/src/components/settings/services/ServiceItem.js index 20d8581d0..9743315b0 100644 --- a/src/components/settings/services/ServiceItem.js +++ b/src/components/settings/services/ServiceItem.js | |||
@@ -16,6 +16,10 @@ const messages = defineMessages({ | |||
16 | id: 'settings.services.tooltip.notificationsDisabled', | 16 | id: 'settings.services.tooltip.notificationsDisabled', |
17 | defaultMessage: '!!!Notifications are disabled', | 17 | defaultMessage: '!!!Notifications are disabled', |
18 | }, | 18 | }, |
19 | tooltipIsMuted: { | ||
20 | id: 'settings.services.tooltip.isMuted', | ||
21 | defaultMessage: '!!!All sounds are muted', | ||
22 | }, | ||
19 | }); | 23 | }); |
20 | 24 | ||
21 | @observer | 25 | @observer |
@@ -66,6 +70,17 @@ export default class ServiceItem extends Component { | |||
66 | className="service-table__column-info" | 70 | className="service-table__column-info" |
67 | onClick={goToServiceForm} | 71 | onClick={goToServiceForm} |
68 | > | 72 | > |
73 | {service.isMuted && ( | ||
74 | <span | ||
75 | className="mdi mdi-bell-off" | ||
76 | data-tip={intl.formatMessage(messages.tooltipIsMuted)} | ||
77 | /> | ||
78 | )} | ||
79 | </td> | ||
80 | <td | ||
81 | className="service-table__column-info" | ||
82 | onClick={goToServiceForm} | ||
83 | > | ||
69 | {!service.isEnabled && ( | 84 | {!service.isEnabled && ( |
70 | <span | 85 | <span |
71 | className="mdi mdi-power" | 86 | className="mdi mdi-power" |
@@ -85,13 +100,6 @@ export default class ServiceItem extends Component { | |||
85 | )} | 100 | )} |
86 | <ReactTooltip place="top" type="dark" effect="solid" /> | 101 | <ReactTooltip place="top" type="dark" effect="solid" /> |
87 | </td> | 102 | </td> |
88 | {/* <td className="service-table__column-action"> | ||
89 | <input | ||
90 | type="checkbox" | ||
91 | onChange={toggleAction} | ||
92 | checked={service.isEnabled} | ||
93 | /> | ||
94 | </td> */} | ||
95 | </tr> | 103 | </tr> |
96 | ); | 104 | ); |
97 | } | 105 | } |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 5675fecf4..4ce9b7ab2 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -9,6 +9,8 @@ import Button from '../../ui/Button'; | |||
9 | import Toggle from '../../ui/Toggle'; | 9 | import Toggle from '../../ui/Toggle'; |
10 | import Select from '../../ui/Select'; | 10 | import Select from '../../ui/Select'; |
11 | 11 | ||
12 | import { FRANZ_TRANSLATION } from '../../../config'; | ||
13 | |||
12 | const messages = defineMessages({ | 14 | const messages = defineMessages({ |
13 | headline: { | 15 | headline: { |
14 | id: 'settings.app.headline', | 16 | id: 'settings.app.headline', |
@@ -26,6 +28,18 @@ const messages = defineMessages({ | |||
26 | id: 'settings.app.headlineUpdates', | 28 | id: 'settings.app.headlineUpdates', |
27 | defaultMessage: '!!!Updates', | 29 | defaultMessage: '!!!Updates', |
28 | }, | 30 | }, |
31 | headlineAppearance: { | ||
32 | id: 'settings.app.headlineAppearance', | ||
33 | defaultMessage: '!!!Appearance', | ||
34 | }, | ||
35 | headlineAdvanced: { | ||
36 | id: 'settings.app.headlineAdvanced', | ||
37 | defaultMessage: '!!!Advanced', | ||
38 | }, | ||
39 | translationHelp: { | ||
40 | id: 'settings.app.translationHelp', | ||
41 | defaultMessage: '!!!Help us to translate Franz into your language.', | ||
42 | }, | ||
29 | buttonSearchForUpdate: { | 43 | buttonSearchForUpdate: { |
30 | id: 'settings.app.buttonSearchForUpdate', | 44 | id: 'settings.app.buttonSearchForUpdate', |
31 | defaultMessage: '!!!Check for updates', | 45 | defaultMessage: '!!!Check for updates', |
@@ -50,6 +64,10 @@ const messages = defineMessages({ | |||
50 | id: 'settings.app.currentVersion', | 64 | id: 'settings.app.currentVersion', |
51 | defaultMessage: '!!!Current version:', | 65 | defaultMessage: '!!!Current version:', |
52 | }, | 66 | }, |
67 | restartRequired: { | ||
68 | id: 'settings.app.restartRequired', | ||
69 | defaultMessage: '!!!Changes require restart', | ||
70 | }, | ||
53 | }); | 71 | }); |
54 | 72 | ||
55 | @observer | 73 | @observer |
@@ -112,16 +130,38 @@ export default class EditSettingsForm extends Component { | |||
112 | onChange={e => this.submit(e)} | 130 | onChange={e => this.submit(e)} |
113 | id="form" | 131 | id="form" |
114 | > | 132 | > |
115 | <h2>{intl.formatMessage(messages.headlineGeneral)}</h2> | 133 | {/* General */} |
134 | <h2 id="general">{intl.formatMessage(messages.headlineGeneral)}</h2> | ||
116 | <Toggle field={form.$('autoLaunchOnStart')} /> | 135 | <Toggle field={form.$('autoLaunchOnStart')} /> |
117 | <Toggle field={form.$('runInBackground')} /> | 136 | <Toggle field={form.$('runInBackground')} /> |
118 | <Toggle field={form.$('enableSystemTray')} /> | 137 | <Toggle field={form.$('enableSystemTray')} /> |
119 | {process.platform === 'win32' && ( | 138 | {process.platform === 'win32' && ( |
120 | <Toggle field={form.$('minimizeToSystemTray')} /> | 139 | <Toggle field={form.$('minimizeToSystemTray')} /> |
121 | )} | 140 | )} |
122 | <h2>{intl.formatMessage(messages.headlineLanguage)}</h2> | 141 | |
142 | {/* Appearance */} | ||
143 | <h2 id="apperance">{intl.formatMessage(messages.headlineAppearance)}</h2> | ||
144 | <Toggle field={form.$('showDisabledServices')} /> | ||
145 | |||
146 | {/* Language */} | ||
147 | <h2 id="language">{intl.formatMessage(messages.headlineLanguage)}</h2> | ||
123 | <Select field={form.$('locale')} showLabel={false} /> | 148 | <Select field={form.$('locale')} showLabel={false} /> |
124 | <h2>{intl.formatMessage(messages.headlineUpdates)}</h2> | 149 | <a |
150 | href={FRANZ_TRANSLATION} | ||
151 | target="_blank" | ||
152 | className="link" | ||
153 | > | ||
154 | {intl.formatMessage(messages.translationHelp)} <i className="mdi mdi-open-in-new" /> | ||
155 | </a> | ||
156 | |||
157 | {/* Advanced */} | ||
158 | <h2 id="advanced">{intl.formatMessage(messages.headlineAdvanced)}</h2> | ||
159 | <Toggle field={form.$('enableSpellchecking')} /> | ||
160 | <p className="settings__help">{intl.formatMessage(messages.restartRequired)}</p> | ||
161 | {/* <Select field={form.$('spellcheckingLanguage')} /> */} | ||
162 | |||
163 | {/* Updates */} | ||
164 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> | ||
125 | {updateIsReadyToInstall ? ( | 165 | {updateIsReadyToInstall ? ( |
126 | <Button | 166 | <Button |
127 | label={intl.formatMessage(messages.buttonInstallUpdate)} | 167 | label={intl.formatMessage(messages.buttonInstallUpdate)} |
diff --git a/src/components/ui/AppLoader.js b/src/components/ui/AppLoader.js index 64a212969..ac3cdcb05 100644 --- a/src/components/ui/AppLoader.js +++ b/src/components/ui/AppLoader.js | |||
@@ -8,7 +8,7 @@ export default function () { | |||
8 | <div className="app-loader"> | 8 | <div className="app-loader"> |
9 | <Appear> | 9 | <Appear> |
10 | <h1 className="app-loader__title">Franz</h1> | 10 | <h1 className="app-loader__title">Franz</h1> |
11 | <Loader /> | 11 | <Loader color="#FFF" /> |
12 | </Appear> | 12 | </Appear> |
13 | </div> | 13 | </div> |
14 | ); | 14 | ); |
diff --git a/src/components/ui/InfoBar.js b/src/components/ui/InfoBar.js index aea2bd888..84a5f1446 100644 --- a/src/components/ui/InfoBar.js +++ b/src/components/ui/InfoBar.js | |||
@@ -61,10 +61,13 @@ export default class InfoBar extends Component { | |||
61 | [`${className}`]: true, | 61 | [`${className}`]: true, |
62 | })} | 62 | })} |
63 | > | 63 | > |
64 | <div onClick={onClick} className="info-bar__content"> | 64 | <div className="info-bar__content"> |
65 | {children} | 65 | {children} |
66 | {ctaLabel && ( | 66 | {ctaLabel && ( |
67 | <button className="info-bar__cta"> | 67 | <button |
68 | className="info-bar__cta" | ||
69 | onClick={onClick} | ||
70 | > | ||
68 | <Loader | 71 | <Loader |
69 | loaded={!ctaLoading} | 72 | loaded={!ctaLoading} |
70 | lines={10} | 73 | lines={10} |
diff --git a/src/components/ui/Loader.js b/src/components/ui/Loader.js index e4fbd96a2..f73296bb6 100644 --- a/src/components/ui/Loader.js +++ b/src/components/ui/Loader.js | |||
@@ -9,12 +9,14 @@ export default class LoaderComponent extends Component { | |||
9 | children: oneOrManyChildElements, | 9 | children: oneOrManyChildElements, |
10 | loaded: PropTypes.bool, | 10 | loaded: PropTypes.bool, |
11 | className: PropTypes.string, | 11 | className: PropTypes.string, |
12 | color: PropTypes.string, | ||
12 | }; | 13 | }; |
13 | 14 | ||
14 | static defaultProps = { | 15 | static defaultProps = { |
15 | children: null, | 16 | children: null, |
16 | loaded: false, | 17 | loaded: false, |
17 | className: '', | 18 | className: '', |
19 | color: '#373a3c', | ||
18 | }; | 20 | }; |
19 | 21 | ||
20 | render() { | 22 | render() { |
@@ -22,6 +24,7 @@ export default class LoaderComponent extends Component { | |||
22 | children, | 24 | children, |
23 | loaded, | 25 | loaded, |
24 | className, | 26 | className, |
27 | color, | ||
25 | } = this.props; | 28 | } = this.props; |
26 | 29 | ||
27 | return ( | 30 | return ( |
@@ -30,7 +33,7 @@ export default class LoaderComponent extends Component { | |||
30 | // lines={10} | 33 | // lines={10} |
31 | width={4} | 34 | width={4} |
32 | scale={0.6} | 35 | scale={0.6} |
33 | color="#373a3c" | 36 | color={color} |
34 | component="span" | 37 | component="span" |
35 | className={className} | 38 | className={className} |
36 | > | 39 | > |
diff --git a/src/components/ui/Subscription.js b/src/components/ui/Subscription.js index fe0925a26..8bff72095 100644 --- a/src/components/ui/Subscription.js +++ b/src/components/ui/Subscription.js | |||
@@ -93,6 +93,10 @@ const messages = defineMessages({ | |||
93 | id: 'subscription.mining.moreInformation', | 93 | id: 'subscription.mining.moreInformation', |
94 | defaultMessage: '!!!Get more information about this plan', | 94 | defaultMessage: '!!!Get more information about this plan', |
95 | }, | 95 | }, |
96 | euTaxInfo: { | ||
97 | id: 'subscription.euTaxInfo', | ||
98 | defaultMessage: '!!!EU residents: local sales tax may apply', | ||
99 | }, | ||
96 | }); | 100 | }); |
97 | 101 | ||
98 | @observer | 102 | @observer |
@@ -144,14 +148,18 @@ export default class SubscriptionForm extends Component { | |||
144 | label: `€ ${Object.hasOwnProperty.call(this.props.plan, 'year') | 148 | label: `€ ${Object.hasOwnProperty.call(this.props.plan, 'year') |
145 | ? `${this.props.plan.year.price} / ${intl.formatMessage(messages.typeYearly)}` | 149 | ? `${this.props.plan.year.price} / ${intl.formatMessage(messages.typeYearly)}` |
146 | : 'yearly'}`, | 150 | : 'yearly'}`, |
147 | }, { | ||
148 | value: 'mining', | ||
149 | label: intl.formatMessage(messages.typeMining), | ||
150 | }], | 151 | }], |
151 | }, | 152 | }, |
152 | }, | 153 | }, |
153 | }; | 154 | }; |
154 | 155 | ||
156 | if (this.props.plan.miner) { | ||
157 | form.fields.paymentTier.options.push({ | ||
158 | value: 'mining', | ||
159 | label: intl.formatMessage(messages.typeMining), | ||
160 | }); | ||
161 | } | ||
162 | |||
155 | if (this.props.showSkipOption) { | 163 | if (this.props.showSkipOption) { |
156 | form.fields.paymentTier.options.unshift({ | 164 | form.fields.paymentTier.options.unshift({ |
157 | value: 'skip', | 165 | value: 'skip', |
@@ -259,6 +267,11 @@ export default class SubscriptionForm extends Component { | |||
259 | onClick={() => handlePayment(this.form.$('paymentTier').value)} | 267 | onClick={() => handlePayment(this.form.$('paymentTier').value)} |
260 | /> | 268 | /> |
261 | )} | 269 | )} |
270 | {this.form.$('paymentTier').value !== 'skip' && this.form.$('paymentTier').value !== 'mining' && ( | ||
271 | <p className="legal"> | ||
272 | {intl.formatMessage(messages.euTaxInfo)} | ||
273 | </p> | ||
274 | )} | ||
262 | </Loader> | 275 | </Loader> |
263 | ); | 276 | ); |
264 | } | 277 | } |