diff options
Diffstat (limited to 'src/components')
-rw-r--r-- | src/components/AppUpdateInfoBar.js | 4 | ||||
-rw-r--r-- | src/components/auth/AuthLayout.js | 9 | ||||
-rw-r--r-- | src/components/auth/ChangeServer.js | 80 | ||||
-rw-r--r-- | src/components/auth/Login.js | 4 | ||||
-rw-r--r-- | src/components/auth/Signup.js | 7 | ||||
-rw-r--r-- | src/components/auth/Welcome.js | 4 | ||||
-rw-r--r-- | src/components/layout/AppLayout.js | 9 | ||||
-rw-r--r-- | src/components/services/content/ConnectionLostBanner.js | 119 | ||||
-rw-r--r-- | src/components/services/content/ServiceView.js | 8 | ||||
-rw-r--r-- | src/components/services/tabs/TabItem.js | 2 | ||||
-rw-r--r-- | src/components/settings/account/AccountDashboard.js | 43 | ||||
-rw-r--r-- | src/components/settings/navigation/SettingsNavigation.js | 25 | ||||
-rw-r--r-- | src/components/settings/services/EditServiceForm.js | 31 | ||||
-rw-r--r-- | src/components/settings/settings/EditSettingsForm.js | 168 | ||||
-rw-r--r-- | src/components/settings/team/TeamDashboard.js | 6 | ||||
-rw-r--r-- | src/components/ui/FeatureList.js | 5 | ||||
-rw-r--r-- | src/components/ui/Slider.js | 65 |
17 files changed, 440 insertions, 149 deletions
diff --git a/src/components/AppUpdateInfoBar.js b/src/components/AppUpdateInfoBar.js index 4108fdf12..f51fe029b 100644 --- a/src/components/AppUpdateInfoBar.js +++ b/src/components/AppUpdateInfoBar.js | |||
@@ -24,6 +24,7 @@ class AppUpdateInfoBar extends Component { | |||
24 | static propTypes = { | 24 | static propTypes = { |
25 | onInstallUpdate: PropTypes.func.isRequired, | 25 | onInstallUpdate: PropTypes.func.isRequired, |
26 | nextAppReleaseVersion: PropTypes.string, | 26 | nextAppReleaseVersion: PropTypes.string, |
27 | onHide: PropTypes.func.isRequired, | ||
27 | }; | 28 | }; |
28 | 29 | ||
29 | static defaultProps = { | 30 | static defaultProps = { |
@@ -39,6 +40,7 @@ class AppUpdateInfoBar extends Component { | |||
39 | const { | 40 | const { |
40 | onInstallUpdate, | 41 | onInstallUpdate, |
41 | nextAppReleaseVersion, | 42 | nextAppReleaseVersion, |
43 | onHide, | ||
42 | } = this.props; | 44 | } = this.props; |
43 | 45 | ||
44 | return ( | 46 | return ( |
@@ -46,7 +48,7 @@ class AppUpdateInfoBar extends Component { | |||
46 | type="primary" | 48 | type="primary" |
47 | ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)} | 49 | ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)} |
48 | onClick={onInstallUpdate} | 50 | onClick={onInstallUpdate} |
49 | sticky | 51 | onHide={onHide} |
50 | > | 52 | > |
51 | <span className="mdi mdi-information" /> | 53 | <span className="mdi mdi-information" /> |
52 | {intl.formatMessage(messages.updateAvailable)} | 54 | {intl.formatMessage(messages.updateAvailable)} |
diff --git a/src/components/auth/AuthLayout.js b/src/components/auth/AuthLayout.js index 0c5198583..4783fc6a0 100644 --- a/src/components/auth/AuthLayout.js +++ b/src/components/auth/AuthLayout.js | |||
@@ -27,6 +27,10 @@ export default @observer class AuthLayout extends Component { | |||
27 | appUpdateIsDownloaded: PropTypes.bool.isRequired, | 27 | appUpdateIsDownloaded: PropTypes.bool.isRequired, |
28 | }; | 28 | }; |
29 | 29 | ||
30 | state = { | ||
31 | shouldShowAppUpdateInfoBar: true, | ||
32 | } | ||
33 | |||
30 | static defaultProps = { | 34 | static defaultProps = { |
31 | nextAppReleaseVersion: null, | 35 | nextAppReleaseVersion: null, |
32 | }; | 36 | }; |
@@ -62,10 +66,13 @@ export default @observer class AuthLayout extends Component { | |||
62 | {intl.formatMessage(globalMessages.notConnectedToTheInternet)} | 66 | {intl.formatMessage(globalMessages.notConnectedToTheInternet)} |
63 | </InfoBar> | 67 | </InfoBar> |
64 | )} | 68 | )} |
65 | {appUpdateIsDownloaded && ( | 69 | {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( |
66 | <AppUpdateInfoBar | 70 | <AppUpdateInfoBar |
67 | nextAppReleaseVersion={nextAppReleaseVersion} | 71 | nextAppReleaseVersion={nextAppReleaseVersion} |
68 | onInstallUpdate={installAppUpdate} | 72 | onInstallUpdate={installAppUpdate} |
73 | onHide={() => { | ||
74 | this.setState({ shouldShowAppUpdateInfoBar: false }); | ||
75 | }} | ||
69 | /> | 76 | /> |
70 | )} | 77 | )} |
71 | {isOnline && !isAPIHealthy && ( | 78 | {isOnline && !isAPIHealthy && ( |
diff --git a/src/components/auth/ChangeServer.js b/src/components/auth/ChangeServer.js new file mode 100644 index 000000000..433334b6c --- /dev/null +++ b/src/components/auth/ChangeServer.js | |||
@@ -0,0 +1,80 @@ | |||
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 Form from '../../lib/Form'; | ||
7 | import Input from '../ui/Input'; | ||
8 | import Button from '../ui/Button'; | ||
9 | |||
10 | const messages = defineMessages({ | ||
11 | headline: { | ||
12 | id: 'changeserver.headline', | ||
13 | defaultMessage: '!!!Change server', | ||
14 | }, | ||
15 | label: { | ||
16 | id: 'changeserver.label', | ||
17 | defaultMessage: '!!!Server', | ||
18 | }, | ||
19 | submit: { | ||
20 | id: 'changeserver.submit', | ||
21 | defaultMessage: '!!!Submit', | ||
22 | }, | ||
23 | }); | ||
24 | |||
25 | export default @observer class ChangeServer extends Component { | ||
26 | static propTypes = { | ||
27 | onSubmit: PropTypes.func.isRequired, | ||
28 | server: PropTypes.string.isRequired, | ||
29 | }; | ||
30 | |||
31 | static contextTypes = { | ||
32 | intl: intlShape, | ||
33 | }; | ||
34 | |||
35 | form = new Form({ | ||
36 | fields: { | ||
37 | server: { | ||
38 | label: this.context.intl.formatMessage(messages.label), | ||
39 | value: '', | ||
40 | }, | ||
41 | }, | ||
42 | }, this.context.intl); | ||
43 | |||
44 | componentDidMount() { | ||
45 | this.form.$('server').value = this.props.server; | ||
46 | } | ||
47 | |||
48 | submit(e) { | ||
49 | e.preventDefault(); | ||
50 | this.form.submit({ | ||
51 | onSuccess: (form) => { | ||
52 | this.props.onSubmit(form.values()); | ||
53 | }, | ||
54 | onError: () => { }, | ||
55 | }); | ||
56 | } | ||
57 | |||
58 | render() { | ||
59 | const { form } = this; | ||
60 | const { intl } = this.context; | ||
61 | |||
62 | return ( | ||
63 | <div className="auth__container"> | ||
64 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> | ||
65 | <h1>{intl.formatMessage(messages.headline)}</h1> | ||
66 | |||
67 | <Input | ||
68 | field={form.$('server')} | ||
69 | focus | ||
70 | /> | ||
71 | <Button | ||
72 | type="submit" | ||
73 | className="auth__button" | ||
74 | label={intl.formatMessage(messages.submit)} | ||
75 | /> | ||
76 | </form> | ||
77 | </div> | ||
78 | ); | ||
79 | } | ||
80 | } | ||
diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js index e25121de0..f33d134c8 100644 --- a/src/components/auth/Login.js +++ b/src/components/auth/Login.js | |||
@@ -78,6 +78,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
78 | isServerLogout: PropTypes.bool.isRequired, | 78 | isServerLogout: PropTypes.bool.isRequired, |
79 | signupRoute: PropTypes.string.isRequired, | 79 | signupRoute: PropTypes.string.isRequired, |
80 | passwordRoute: PropTypes.string.isRequired, | 80 | passwordRoute: PropTypes.string.isRequired, |
81 | changeServerRoute: PropTypes.string.isRequired, | ||
81 | error: globalErrorPropType.isRequired, | 82 | error: globalErrorPropType.isRequired, |
82 | actions: PropTypes.object.isRequired, | 83 | actions: PropTypes.object.isRequired, |
83 | }; | 84 | }; |
@@ -127,6 +128,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
127 | isServerLogout, | 128 | isServerLogout, |
128 | signupRoute, | 129 | signupRoute, |
129 | passwordRoute, | 130 | passwordRoute, |
131 | changeServerRoute, | ||
130 | error, | 132 | error, |
131 | } = this.props; | 133 | } = this.props; |
132 | 134 | ||
@@ -194,7 +196,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
194 | )} | 196 | )} |
195 | </form> | 197 | </form> |
196 | <div className="auth__links"> | 198 | <div className="auth__links"> |
197 | <Link to="/settings/app">{intl.formatMessage(messages.changeServer)}</Link> | 199 | <Link to={changeServerRoute}>{intl.formatMessage(messages.changeServer)}</Link> |
198 | <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> | 200 | <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> |
199 | <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link> | 201 | <Link to={signupRoute}>{intl.formatMessage(messages.signupLink)}</Link> |
200 | <Link to={passwordRoute}>{intl.formatMessage(messages.passwordLink)}</Link> | 202 | <Link to={passwordRoute}>{intl.formatMessage(messages.passwordLink)}</Link> |
diff --git a/src/components/auth/Signup.js b/src/components/auth/Signup.js index a166155a7..6a7db5cde 100644 --- a/src/components/auth/Signup.js +++ b/src/components/auth/Signup.js | |||
@@ -79,6 +79,7 @@ export default @inject('actions') @observer class Signup extends Component { | |||
79 | onSubmit: PropTypes.func.isRequired, | 79 | onSubmit: PropTypes.func.isRequired, |
80 | isSubmitting: PropTypes.bool.isRequired, | 80 | isSubmitting: PropTypes.bool.isRequired, |
81 | loginRoute: PropTypes.string.isRequired, | 81 | loginRoute: PropTypes.string.isRequired, |
82 | changeServerRoute: PropTypes.string.isRequired, | ||
82 | error: globalErrorPropType.isRequired, | 83 | error: globalErrorPropType.isRequired, |
83 | actions: PropTypes.object.isRequired, | 84 | actions: PropTypes.object.isRequired, |
84 | }; | 85 | }; |
@@ -130,7 +131,9 @@ export default @inject('actions') @observer class Signup extends Component { | |||
130 | render() { | 131 | render() { |
131 | const { form } = this; | 132 | const { form } = this; |
132 | const { intl } = this.context; | 133 | const { intl } = this.context; |
133 | const { isSubmitting, loginRoute, error } = this.props; | 134 | const { |
135 | isSubmitting, loginRoute, error, changeServerRoute, | ||
136 | } = this.props; | ||
134 | 137 | ||
135 | const termsBase = window.ferdi.stores.settings.all.app.server !== 'https://api.franzinfra.com' ? window.ferdi.stores.settings.all.app.server : 'https://meetfranz.com'; | 138 | const termsBase = window.ferdi.stores.settings.all.app.server !== 'https://api.franzinfra.com' ? window.ferdi.stores.settings.all.app.server : 'https://meetfranz.com'; |
136 | 139 | ||
@@ -198,7 +201,7 @@ export default @inject('actions') @observer class Signup extends Component { | |||
198 | </p> | 201 | </p> |
199 | </form> | 202 | </form> |
200 | <div className="auth__links"> | 203 | <div className="auth__links"> |
201 | <Link to="/settings/app">{intl.formatMessage(messages.changeServer)}</Link> | 204 | <Link to={changeServerRoute}>{intl.formatMessage(messages.changeServer)}</Link> |
202 | <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> | 205 | <a onClick={this.useLocalServer.bind(this)}>{intl.formatMessage(messages.serverless)}</a> |
203 | <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link> | 206 | <Link to={loginRoute}>{intl.formatMessage(messages.loginLink)}</Link> |
204 | </div> | 207 | </div> |
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js index 1453c1d7c..6e742e0c1 100644 --- a/src/components/auth/Welcome.js +++ b/src/components/auth/Welcome.js | |||
@@ -26,6 +26,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
26 | static propTypes = { | 26 | static propTypes = { |
27 | loginRoute: PropTypes.string.isRequired, | 27 | loginRoute: PropTypes.string.isRequired, |
28 | signupRoute: PropTypes.string.isRequired, | 28 | signupRoute: PropTypes.string.isRequired, |
29 | changeServerRoute: PropTypes.string.isRequired, | ||
29 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, | 30 | recipes: MobxPropTypes.arrayOrObservableArray.isRequired, |
30 | actions: PropTypes.object.isRequired, | 31 | actions: PropTypes.object.isRequired, |
31 | }; | 32 | }; |
@@ -43,6 +44,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
43 | const { | 44 | const { |
44 | loginRoute, | 45 | loginRoute, |
45 | signupRoute, | 46 | signupRoute, |
47 | changeServerRoute, | ||
46 | recipes, | 48 | recipes, |
47 | } = this.props; | 49 | } = this.props; |
48 | 50 | ||
@@ -71,7 +73,7 @@ export default @inject('actions') @observer class Login extends Component { | |||
71 | <br /> | 73 | <br /> |
72 | 74 | ||
73 | 75 | ||
74 | <Link to="settings/app"> | 76 | <Link to={changeServerRoute}> |
75 | <span style={{ | 77 | <span style={{ |
76 | textAlign: 'center', | 78 | textAlign: 'center', |
77 | width: '100%', | 79 | width: '100%', |
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index fe43c42d2..3b732e602 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -81,6 +81,10 @@ class AppLayout extends Component { | |||
81 | hasActivatedTrial: PropTypes.bool.isRequired, | 81 | hasActivatedTrial: PropTypes.bool.isRequired, |
82 | }; | 82 | }; |
83 | 83 | ||
84 | state = { | ||
85 | shouldShowAppUpdateInfoBar: true, | ||
86 | } | ||
87 | |||
84 | static defaultProps = { | 88 | static defaultProps = { |
85 | children: [], | 89 | children: [], |
86 | nextAppReleaseVersion: null, | 90 | nextAppReleaseVersion: null, |
@@ -181,10 +185,13 @@ class AppLayout extends Component { | |||
181 | {intl.formatMessage(messages.servicesUpdated)} | 185 | {intl.formatMessage(messages.servicesUpdated)} |
182 | </InfoBar> | 186 | </InfoBar> |
183 | )} | 187 | )} |
184 | {appUpdateIsDownloaded && ( | 188 | { appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( |
185 | <AppUpdateInfoBar | 189 | <AppUpdateInfoBar |
186 | nextAppReleaseVersion={nextAppReleaseVersion} | 190 | nextAppReleaseVersion={nextAppReleaseVersion} |
187 | onInstallUpdate={installAppUpdate} | 191 | onInstallUpdate={installAppUpdate} |
192 | onHide={() => { | ||
193 | this.setState({ shouldShowAppUpdateInfoBar: false }); | ||
194 | }} | ||
188 | /> | 195 | /> |
189 | )} | 196 | )} |
190 | <BasicAuth /> | 197 | <BasicAuth /> |
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js new file mode 100644 index 000000000..9609a65b1 --- /dev/null +++ b/src/components/services/content/ConnectionLostBanner.js | |||
@@ -0,0 +1,119 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | import { intlShape, defineMessages } from 'react-intl'; | ||
7 | |||
8 | import { | ||
9 | mdiAlert, | ||
10 | } from '@mdi/js'; | ||
11 | import { LIVE_API_WEBSITE } from '../../../config'; | ||
12 | // import { Button } from '@meetfranz/forms'; | ||
13 | |||
14 | const messages = defineMessages({ | ||
15 | text: { | ||
16 | id: 'connectionLostBanner.message', | ||
17 | defaultMessage: '!!!Oh no! Franz lost the connection to {name}.', | ||
18 | }, | ||
19 | moreInformation: { | ||
20 | id: 'connectionLostBanner.informationLink', | ||
21 | defaultMessage: '!!!What happened?', | ||
22 | }, | ||
23 | cta: { | ||
24 | id: 'connectionLostBanner.cta', | ||
25 | defaultMessage: '!!!Reload Service', | ||
26 | }, | ||
27 | }); | ||
28 | |||
29 | const styles = theme => ({ | ||
30 | root: { | ||
31 | background: theme.colorBackground, | ||
32 | borderRadius: theme.borderRadius, | ||
33 | position: 'absolute', | ||
34 | zIndex: 300, | ||
35 | height: 50, | ||
36 | display: 'flex', | ||
37 | flexDirection: 'row', | ||
38 | alignItems: 'center', | ||
39 | bottom: 10, | ||
40 | right: 10, | ||
41 | justifyContent: 'center', | ||
42 | padding: 10, | ||
43 | fontSize: 12, | ||
44 | }, | ||
45 | link: { | ||
46 | display: 'inline-flex', | ||
47 | opacity: 0.7, | ||
48 | }, | ||
49 | button: { | ||
50 | transition: 'opacity 0.25s', | ||
51 | color: theme.colorText, | ||
52 | border: [1, 'solid', theme.colorText], | ||
53 | borderRadius: theme.borderRadiusSmall, | ||
54 | padding: 4, | ||
55 | fontSize: 12, | ||
56 | marginLeft: 15, | ||
57 | |||
58 | '&:hover': { | ||
59 | opacity: 0.8, | ||
60 | }, | ||
61 | }, | ||
62 | icon: { | ||
63 | marginRight: 10, | ||
64 | fill: theme.styleTypes.danger.accent, | ||
65 | }, | ||
66 | }); | ||
67 | |||
68 | @injectSheet(styles) @observer | ||
69 | class ConnectionLostBanner extends Component { | ||
70 | static propTypes = { | ||
71 | classes: PropTypes.object.isRequired, | ||
72 | name: PropTypes.string.isRequired, | ||
73 | reload: PropTypes.func.isRequired, | ||
74 | } | ||
75 | |||
76 | static contextTypes = { | ||
77 | intl: intlShape, | ||
78 | }; | ||
79 | |||
80 | inputRef = React.createRef(); | ||
81 | |||
82 | render() { | ||
83 | const { | ||
84 | classes, | ||
85 | name, | ||
86 | reload, | ||
87 | } = this.props; | ||
88 | |||
89 | const { intl } = this.context; | ||
90 | |||
91 | return ( | ||
92 | <div className={classes.root}> | ||
93 | <Icon | ||
94 | icon={mdiAlert} | ||
95 | className={classes.icon} | ||
96 | /> | ||
97 | <p> | ||
98 | {intl.formatMessage(messages.text, { name })} | ||
99 | <br /> | ||
100 | <a | ||
101 | href={`${LIVE_API_WEBSITE}/support#what-does-franz-lost-the-connection-to-service-mean`} | ||
102 | className={classes.link} | ||
103 | > | ||
104 | {intl.formatMessage(messages.moreInformation)} | ||
105 | </a> | ||
106 | </p> | ||
107 | <button | ||
108 | type="button" | ||
109 | className={classes.button} | ||
110 | onClick={reload} | ||
111 | > | ||
112 | {intl.formatMessage(messages.cta)} | ||
113 | </button> | ||
114 | </div> | ||
115 | ); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | export default ConnectionLostBanner; | ||
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js index f6832038a..d91016c71 100644 --- a/src/components/services/content/ServiceView.js +++ b/src/components/services/content/ServiceView.js | |||
@@ -193,7 +193,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
193 | </Fragment> | 193 | </Fragment> |
194 | ) : ( | 194 | ) : ( |
195 | <> | 195 | <> |
196 | {!service.isHibernating ? ( | 196 | {(!service.isHibernating || service.disableHibernation) ? ( |
197 | <> | 197 | <> |
198 | {showNavBar && ( | 198 | {showNavBar && ( |
199 | <WebControlsScreen service={service} /> | 199 | <WebControlsScreen service={service} /> |
@@ -203,6 +203,12 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
203 | setWebviewReference={setWebviewReference} | 203 | setWebviewReference={setWebviewReference} |
204 | detachService={detachService} | 204 | detachService={detachService} |
205 | /> | 205 | /> |
206 | {/* {service.lostRecipeConnection && ( | ||
207 | <ConnectionLostBanner | ||
208 | name={service.name} | ||
209 | reload={reload} | ||
210 | /> | ||
211 | )} */} | ||
206 | </> | 212 | </> |
207 | ) : ( | 213 | ) : ( |
208 | <div> | 214 | <div> |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 36338a910..ea7a66a62 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -145,7 +145,7 @@ class TabItem extends Component { | |||
145 | • | 145 | • |
146 | </span> | 146 | </span> |
147 | )} | 147 | )} |
148 | {service.isHibernating && ( | 148 | {service.isHibernating && !service.disableHibernation && ( |
149 | <span className="tab-item__message-count hibernating"> | 149 | <span className="tab-item__message-count hibernating"> |
150 | • | 150 | • |
151 | </span> | 151 | </span> |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 7d6bad883..5c3dc21d0 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -154,6 +154,7 @@ class AccountDashboard extends Component { | |||
154 | } | 154 | } |
155 | 155 | ||
156 | const isUsingWithoutAccount = server === LOCAL_SERVER; | 156 | const isUsingWithoutAccount = server === LOCAL_SERVER; |
157 | const isUsingFranzServer = server === 'https://api.franzinfra.com'; | ||
157 | 158 | ||
158 | return ( | 159 | return ( |
159 | <div className="settings__main"> | 160 | <div className="settings__main"> |
@@ -208,7 +209,7 @@ class AccountDashboard extends Component { | |||
208 | </div> | 209 | </div> |
209 | <div className="account__info"> | 210 | <div className="account__info"> |
210 | <H1> | 211 | <H1> |
211 | <span className="username">{`${user.firstname} ${user.lastname}`}</span> | 212 | <span className="username">{`${user.firstname} ${isUsingFranzServer ? user.lastname : ''}`}</span> |
212 | {user.isPremium && ( | 213 | {user.isPremium && ( |
213 | <> | 214 | <> |
214 | {' '} | 215 | {' '} |
@@ -243,7 +244,7 @@ class AccountDashboard extends Component { | |||
243 | )} | 244 | )} |
244 | </div> | 245 | </div> |
245 | </div> | 246 | </div> |
246 | {user.isPremium && user.isSubscriptionOwner && ( | 247 | {user.isPremium && user.isSubscriptionOwner && isUsingFranzServer && ( |
247 | <div className="account"> | 248 | <div className="account"> |
248 | <div className="account__box"> | 249 | <div className="account__box"> |
249 | <H2>{intl.formatMessage(messages.yourLicense)}</H2> | 250 | <H2>{intl.formatMessage(messages.yourLicense)}</H2> |
@@ -322,25 +323,27 @@ class AccountDashboard extends Component { | |||
322 | </> | 323 | </> |
323 | )} | 324 | )} |
324 | 325 | ||
325 | <div className="account franz-form"> | 326 | {isUsingFranzServer && ( |
326 | <div className="account__box"> | 327 | <div className="account franz-form"> |
327 | <H2>{intl.formatMessage(messages.headlineDangerZone)}</H2> | 328 | <div className="account__box"> |
328 | {!isDeleteAccountSuccessful && ( | 329 | <H2>{intl.formatMessage(messages.headlineDangerZone)}</H2> |
329 | <div className="account__subscription"> | 330 | {!isDeleteAccountSuccessful && ( |
330 | <p>{intl.formatMessage(messages.deleteInfo)}</p> | 331 | <div className="account__subscription"> |
331 | <Button | 332 | <p>{intl.formatMessage(messages.deleteInfo)}</p> |
332 | label={intl.formatMessage(messages.deleteAccount)} | 333 | <Button |
333 | buttonType="danger" | 334 | label={intl.formatMessage(messages.deleteAccount)} |
334 | onClick={() => deleteAccount()} | 335 | buttonType="danger" |
335 | loaded={!isLoadingDeleteAccount} | 336 | onClick={() => deleteAccount()} |
336 | /> | 337 | loaded={!isLoadingDeleteAccount} |
337 | </div> | 338 | /> |
338 | )} | 339 | </div> |
339 | {isDeleteAccountSuccessful && ( | 340 | )} |
340 | <p>{intl.formatMessage(messages.deleteEmailSent)}</p> | 341 | {isDeleteAccountSuccessful && ( |
341 | )} | 342 | <p>{intl.formatMessage(messages.deleteEmailSent)}</p> |
343 | )} | ||
344 | </div> | ||
342 | </div> | 345 | </div> |
343 | </div> | 346 | )} |
344 | </> | 347 | </> |
345 | )} | 348 | )} |
346 | </> | 349 | </> |
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index eb3249fa0..6b03f05be 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -105,6 +105,7 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
105 | const { intl } = this.context; | 105 | const { intl } = this.context; |
106 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); | 106 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); |
107 | const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; | 107 | const isUsingWithoutAccount = stores.settings.app.server === LOCAL_SERVER; |
108 | const isUsingFranzServer = stores.settings.app.server === 'https://api.franzinfra.com'; | ||
108 | 109 | ||
109 | return ( | 110 | return ( |
110 | <div className="settings-navigation"> | 111 | <div className="settings-navigation"> |
@@ -154,17 +155,19 @@ export default @inject('stores', 'actions') @observer class SettingsNavigation e | |||
154 | > | 155 | > |
155 | {intl.formatMessage(messages.account)} | 156 | {intl.formatMessage(messages.account)} |
156 | </Link> | 157 | </Link> |
157 | <Link | 158 | {isUsingFranzServer && ( |
158 | to="/settings/team" | 159 | <Link |
159 | className="settings-navigation__link" | 160 | to="/settings/team" |
160 | activeClassName="is-active" | 161 | className="settings-navigation__link" |
161 | disabled={!isLoggedIn} | 162 | activeClassName="is-active" |
162 | > | 163 | disabled={!isLoggedIn} |
163 | {intl.formatMessage(messages.team)} | 164 | > |
164 | {!user.data.isPremium && ( | 165 | {intl.formatMessage(messages.team)} |
165 | <ProBadge inverted={!isDarkThemeActive && router.location.pathname === '/settings/team'} /> | 166 | {!user.data.isPremium && ( |
166 | )} | 167 | <ProBadge inverted={!isDarkThemeActive && router.location.pathname === '/settings/team'} /> |
167 | </Link> | 168 | )} |
169 | </Link> | ||
170 | )} | ||
168 | <Link | 171 | <Link |
169 | to="/settings/app" | 172 | to="/settings/app" |
170 | className="settings-navigation__link" | 173 | className="settings-navigation__link" |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 98051d78f..4fd1f99ef 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -12,6 +12,7 @@ import Service from '../../../models/Service'; | |||
12 | import Tabs, { TabItem } from '../../ui/Tabs'; | 12 | import Tabs, { TabItem } from '../../ui/Tabs'; |
13 | import Input from '../../ui/Input'; | 13 | import Input from '../../ui/Input'; |
14 | import Toggle from '../../ui/Toggle'; | 14 | import Toggle from '../../ui/Toggle'; |
15 | import Slider from '../../ui/Slider'; | ||
15 | import Button from '../../ui/Button'; | 16 | import Button from '../../ui/Button'; |
16 | import ImageUpload from '../../ui/ImageUpload'; | 17 | import ImageUpload from '../../ui/ImageUpload'; |
17 | import Select from '../../ui/Select'; | 18 | import Select from '../../ui/Select'; |
@@ -93,6 +94,10 @@ const messages = defineMessages({ | |||
93 | id: 'settings.service.form.isMutedInfo', | 94 | id: 'settings.service.form.isMutedInfo', |
94 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', | 95 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', |
95 | }, | 96 | }, |
97 | disableHibernationInfo: { | ||
98 | id: 'settings.service.form.disableHibernationInfo', | ||
99 | defaultMessage: '!!!You currently have hibernation enabled but you can disable hibernation for individual services using this option.', | ||
100 | }, | ||
96 | headlineNotifications: { | 101 | headlineNotifications: { |
97 | id: 'settings.service.form.headlineNotifications', | 102 | id: 'settings.service.form.headlineNotifications', |
98 | defaultMessage: '!!!Notifications', | 103 | defaultMessage: '!!!Notifications', |
@@ -105,6 +110,10 @@ const messages = defineMessages({ | |||
105 | id: 'settings.service.form.headlineGeneral', | 110 | id: 'settings.service.form.headlineGeneral', |
106 | defaultMessage: '!!!General', | 111 | defaultMessage: '!!!General', |
107 | }, | 112 | }, |
113 | headlineDarkReaderSettings: { | ||
114 | id: 'settings.service.form.headlineDarkReaderSettings', | ||
115 | defaultMessage: '!!!DarkReader Settings', | ||
116 | }, | ||
108 | iconDelete: { | 117 | iconDelete: { |
109 | id: 'settings.service.form.iconDelete', | 118 | id: 'settings.service.form.iconDelete', |
110 | defaultMessage: '!!!Delete', | 119 | defaultMessage: '!!!Delete', |
@@ -149,6 +158,7 @@ export default @observer class EditServiceForm extends Component { | |||
149 | isProxyFeatureEnabled: PropTypes.bool.isRequired, | 158 | isProxyFeatureEnabled: PropTypes.bool.isRequired, |
150 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, | 159 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, |
151 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, | 160 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, |
161 | isHibernationFeatureActive: PropTypes.bool.isRequired, | ||
152 | }; | 162 | }; |
153 | 163 | ||
154 | static defaultProps = { | 164 | static defaultProps = { |
@@ -214,6 +224,7 @@ export default @observer class EditServiceForm extends Component { | |||
214 | isProxyFeatureEnabled, | 224 | isProxyFeatureEnabled, |
215 | isServiceProxyIncludedInCurrentPlan, | 225 | isServiceProxyIncludedInCurrentPlan, |
216 | isSpellcheckerIncludedInCurrentPlan, | 226 | isSpellcheckerIncludedInCurrentPlan, |
227 | isHibernationFeatureActive, | ||
217 | } = this.props; | 228 | } = this.props; |
218 | const { intl } = this.context; | 229 | const { intl } = this.context; |
219 | 230 | ||
@@ -359,8 +370,26 @@ export default @observer class EditServiceForm extends Component { | |||
359 | 370 | ||
360 | <div className="settings__settings-group"> | 371 | <div className="settings__settings-group"> |
361 | <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> | 372 | <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> |
362 | <Toggle field={form.$('isDarkModeEnabled')} /> | ||
363 | <Toggle field={form.$('isEnabled')} /> | 373 | <Toggle field={form.$('isEnabled')} /> |
374 | {isHibernationFeatureActive && ( | ||
375 | <> | ||
376 | <Toggle field={form.$('disableHibernation')} /> | ||
377 | <p className="settings__help"> | ||
378 | {intl.formatMessage(messages.disableHibernationInfo)} | ||
379 | </p> | ||
380 | </> | ||
381 | )} | ||
382 | <Toggle field={form.$('isDarkModeEnabled')} /> | ||
383 | {form.$('isDarkModeEnabled').value | ||
384 | && ( | ||
385 | <> | ||
386 | <h3>{intl.formatMessage(messages.headlineDarkReaderSettings)}</h3> | ||
387 | <Slider field={form.$('darkReaderBrightness')} /> | ||
388 | <Slider field={form.$('darkReaderContrast')} /> | ||
389 | <Slider field={form.$('darkReaderSepia')} /> | ||
390 | </> | ||
391 | ) | ||
392 | } | ||
364 | </div> | 393 | </div> |
365 | </div> | 394 | </div> |
366 | <div className="service-icon"> | 395 | <div className="service-icon"> |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 9564d837b..e1c2a2d4f 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -12,21 +12,12 @@ import PremiumFeatureContainer from '../../ui/PremiumFeatureContainer'; | |||
12 | import Input from '../../ui/Input'; | 12 | import Input from '../../ui/Input'; |
13 | 13 | ||
14 | import { FRANZ_TRANSLATION } from '../../../config'; | 14 | import { FRANZ_TRANSLATION } from '../../../config'; |
15 | import { isMac } from '../../../environment'; | 15 | import { isMac, isWindows } from '../../../environment'; |
16 | 16 | ||
17 | const { | 17 | const { |
18 | systemPreferences, | 18 | systemPreferences, |
19 | } = remote; | 19 | } = remote; |
20 | 20 | ||
21 | function escapeHtml(unsafe) { | ||
22 | return unsafe | ||
23 | .replace(/&/g, '&') | ||
24 | .replace(/</g, '<') | ||
25 | .replace(/>/g, '>') | ||
26 | .replace(/"/g, '"') | ||
27 | .replace(/'/g, '''); | ||
28 | } | ||
29 | |||
30 | const messages = defineMessages({ | 21 | const messages = defineMessages({ |
31 | headline: { | 22 | headline: { |
32 | id: 'settings.app.headline', | 23 | id: 'settings.app.headline', |
@@ -48,14 +39,6 @@ const messages = defineMessages({ | |||
48 | id: 'settings.app.inactivityLockInfo', | 39 | id: 'settings.app.inactivityLockInfo', |
49 | defaultMessage: '!!!Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable', | 40 | defaultMessage: '!!!Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable', |
50 | }, | 41 | }, |
51 | serverInfo: { | ||
52 | id: 'settings.app.serverInfo', | ||
53 | defaultMessage: '!!!We advice you to logout after changing your server as your settings might not be saved otherwise.', | ||
54 | }, | ||
55 | serverMoneyInfo: { | ||
56 | id: 'settings.app.serverMoneyInfo', | ||
57 | defaultMessage: '!!!You are using the official Franz Server for Ferdi.\nWe know that Ferdi allows you to use all its features for free but you are still using Franz\'s server resources - which Franz\'s creator has to pay for.\nPlease still consider [Link 1]paying for a Franz account[/Link] or [Link 2]using a self-hosted ferdi-server[/Link] (if you have the knowledge and resources to do so). \nBy using Ferdi, you still profit greatly from Franz\'s recipe store, server resources and its development.', | ||
58 | }, | ||
59 | todoServerInfo: { | 42 | todoServerInfo: { |
60 | id: 'settings.app.todoServerInfo', | 43 | id: 'settings.app.todoServerInfo', |
61 | defaultMessage: '!!!This server will be used for the "Franz Todo" feature. (default: https://app.franztodos.com)', | 44 | defaultMessage: '!!!This server will be used for the "Franz Todo" feature. (default: https://app.franztodos.com)', |
@@ -173,12 +156,11 @@ export default @observer class EditSettingsForm extends Component { | |||
173 | cacheSize: PropTypes.string.isRequired, | 156 | cacheSize: PropTypes.string.isRequired, |
174 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, | 157 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, |
175 | isTodosEnabled: PropTypes.bool.isRequired, | 158 | isTodosEnabled: PropTypes.bool.isRequired, |
159 | isTodosActivated: PropTypes.bool.isRequired, | ||
176 | isWorkspaceEnabled: PropTypes.bool.isRequired, | 160 | isWorkspaceEnabled: PropTypes.bool.isRequired, |
177 | server: PropTypes.string.isRequired, | 161 | automaticUpdates: PropTypes.bool.isRequired, |
178 | noUpdates: PropTypes.bool.isRequired, | ||
179 | hibernationEnabled: PropTypes.bool.isRequired, | 162 | hibernationEnabled: PropTypes.bool.isRequired, |
180 | isDarkmodeEnabled: PropTypes.bool.isRequired, | 163 | isDarkmodeEnabled: PropTypes.bool.isRequired, |
181 | isTrayEnabled: PropTypes.bool.isRequired, | ||
182 | isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, | 164 | isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, |
183 | openProcessManager: PropTypes.func.isRequired, | 165 | openProcessManager: PropTypes.func.isRequired, |
184 | }; | 166 | }; |
@@ -214,12 +196,11 @@ export default @observer class EditSettingsForm extends Component { | |||
214 | isSpellcheckerIncludedInCurrentPlan, | 196 | isSpellcheckerIncludedInCurrentPlan, |
215 | isTodosEnabled, | 197 | isTodosEnabled, |
216 | isWorkspaceEnabled, | 198 | isWorkspaceEnabled, |
217 | server, | 199 | automaticUpdates, |
218 | noUpdates, | ||
219 | hibernationEnabled, | 200 | hibernationEnabled, |
220 | isDarkmodeEnabled, | 201 | isDarkmodeEnabled, |
221 | isTrayEnabled, | ||
222 | openProcessManager, | 202 | openProcessManager, |
203 | isTodosActivated, | ||
223 | } = this.props; | 204 | } = this.props; |
224 | const { intl } = this.context; | 205 | const { intl } = this.context; |
225 | 206 | ||
@@ -232,8 +213,6 @@ export default @observer class EditSettingsForm extends Component { | |||
232 | updateButtonLabelMessage = messages.buttonSearchForUpdate; | 213 | updateButtonLabelMessage = messages.buttonSearchForUpdate; |
233 | } | 214 | } |
234 | 215 | ||
235 | const isLoggedIn = Boolean(localStorage.getItem('authToken')); | ||
236 | |||
237 | const { | 216 | const { |
238 | lockingFeatureEnabled, | 217 | lockingFeatureEnabled, |
239 | scheduledDNDEnabled, | 218 | scheduledDNDEnabled, |
@@ -256,7 +235,7 @@ export default @observer class EditSettingsForm extends Component { | |||
256 | <Toggle field={form.$('runInBackground')} /> | 235 | <Toggle field={form.$('runInBackground')} /> |
257 | <Toggle field={form.$('enableSystemTray')} /> | 236 | <Toggle field={form.$('enableSystemTray')} /> |
258 | <Toggle field={form.$('reloadAfterResume')} /> | 237 | <Toggle field={form.$('reloadAfterResume')} /> |
259 | {isTrayEnabled && <Toggle field={form.$('startMinimized')} />} | 238 | <Toggle field={form.$('startMinimized')} /> |
260 | {process.platform === 'win32' && ( | 239 | {process.platform === 'win32' && ( |
261 | <Toggle field={form.$('minimizeToSystemTray')} /> | 240 | <Toggle field={form.$('minimizeToSystemTray')} /> |
262 | )} | 241 | )} |
@@ -287,74 +266,41 @@ export default @observer class EditSettingsForm extends Component { | |||
287 | 266 | ||
288 | <Hr /> | 267 | <Hr /> |
289 | 268 | ||
290 | <Input | ||
291 | placeholder="Server" | ||
292 | onChange={e => this.submit(e)} | ||
293 | field={form.$('server')} | ||
294 | autoFocus | ||
295 | /> | ||
296 | {isLoggedIn && ( | ||
297 | <p | ||
298 | className="settings__message" | ||
299 | style={{ | ||
300 | borderTop: 0, marginTop: 0, paddingTop: 0, marginBottom: '2rem', | ||
301 | }} | ||
302 | > | ||
303 | { intl.formatMessage(messages.serverInfo) } | ||
304 | </p> | ||
305 | )} | ||
306 | {server === 'https://api.franzinfra.com' && ( | ||
307 | <p | ||
308 | className="settings__message" | ||
309 | style={{ | ||
310 | borderTop: 0, marginTop: 0, paddingTop: 0, marginBottom: '2rem', | ||
311 | }} | ||
312 | > | ||
313 | <span | ||
314 | dangerouslySetInnerHTML={{ | ||
315 | __html: | ||
316 | // Needed to make links work | ||
317 | escapeHtml( | ||
318 | intl.formatMessage(messages.serverMoneyInfo), | ||
319 | ).replace('[Link 1]', '<a href="https://www.meetfranz.com/pricing" target="_blank">') | ||
320 | .replace('[Link 2]', '<a href="https://github.com/getferdi/server" target="_blank">') | ||
321 | .replace(/\[\/Link]/g, '</a>'), | ||
322 | }} | ||
323 | style={{ | ||
324 | whiteSpace: 'pre-wrap', | ||
325 | }} | ||
326 | /> | ||
327 | </p> | ||
328 | )} | ||
329 | |||
330 | <Hr /> | ||
331 | |||
332 | {isWorkspaceEnabled && ( | 269 | {isWorkspaceEnabled && ( |
333 | <Toggle field={form.$('keepAllWorkspacesLoaded')} /> | 270 | <Toggle field={form.$('keepAllWorkspacesLoaded')} /> |
334 | )} | 271 | )} |
335 | 272 | ||
336 | |||
337 | <Hr /> | 273 | <Hr /> |
338 | 274 | ||
339 | {isTodosEnabled && ( | 275 | {isTodosEnabled && ( |
340 | <> | 276 | <> |
341 | <Toggle field={form.$('enableTodos')} /> | 277 | <Toggle field={form.$('enableTodos')} /> |
342 | <Input | 278 | {isTodosActivated && ( |
343 | placeholder="Todo Server" | 279 | <div> |
344 | onChange={e => this.submit(e)} | 280 | <Select field={form.$('predefinedTodoServer')} /> |
345 | field={form.$('todoServer')} | 281 | {form.$('predefinedTodoServer').value === 'isUsingCustomTodoService' && ( |
346 | /> | 282 | <div> |
347 | <p | 283 | <Input |
348 | className="settings__message" | 284 | placeholder="Todo Server" |
349 | style={{ | 285 | onChange={e => this.submit(e)} |
350 | borderTop: 0, marginTop: 0, paddingTop: 0, marginBottom: '2rem', | 286 | field={form.$('customTodoServer')} |
351 | }} | 287 | /> |
352 | > | 288 | <p |
353 | { intl.formatMessage(messages.todoServerInfo) } | 289 | className="settings__message" |
354 | </p> | 290 | style={{ |
291 | borderTop: 0, marginTop: 0, paddingTop: 0, marginBottom: '2rem', | ||
292 | }} | ||
293 | > | ||
294 | { intl.formatMessage(messages.todoServerInfo) } | ||
295 | </p> | ||
296 | </div> | ||
297 | )} | ||
298 | </div> | ||
299 | )} | ||
355 | </> | 300 | </> |
356 | )} | 301 | )} |
357 | 302 | ||
303 | |||
358 | <Hr /> | 304 | <Hr /> |
359 | 305 | ||
360 | <Toggle field={form.$('lockingFeatureEnabled')} /> | 306 | <Toggle field={form.$('lockingFeatureEnabled')} /> |
@@ -455,10 +401,12 @@ export default @observer class EditSettingsForm extends Component { | |||
455 | <Toggle field={form.$('showDisabledServices')} /> | 401 | <Toggle field={form.$('showDisabledServices')} /> |
456 | <Toggle field={form.$('showMessageBadgeWhenMuted')} /> | 402 | <Toggle field={form.$('showMessageBadgeWhenMuted')} /> |
457 | 403 | ||
404 | {isMac && <Toggle field={form.$('showDragArea')} />} | ||
405 | |||
458 | <Hr /> | 406 | <Hr /> |
459 | 407 | ||
460 | {isMac && <Toggle field={form.$('adaptableDarkMode')} />} | 408 | {(isMac || isWindows) && <Toggle field={form.$('adaptableDarkMode')} />} |
461 | {!(isMac && isAdaptableDarkModeEnabled) && <Toggle field={form.$('darkMode')} />} | 409 | {!((isMac || isWindows) && isAdaptableDarkModeEnabled) && <Toggle field={form.$('darkMode')} />} |
462 | {(isDarkmodeEnabled || isAdaptableDarkModeEnabled) && ( | 410 | {(isDarkmodeEnabled || isAdaptableDarkModeEnabled) && ( |
463 | <> | 411 | <> |
464 | <Toggle field={form.$('universalDarkMode')} /> | 412 | <Toggle field={form.$('universalDarkMode')} /> |
@@ -555,36 +503,46 @@ export default @observer class EditSettingsForm extends Component { | |||
555 | 503 | ||
556 | {/* Updates */} | 504 | {/* Updates */} |
557 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> | 505 | <h2 id="updates">{intl.formatMessage(messages.headlineUpdates)}</h2> |
558 | {updateIsReadyToInstall ? ( | 506 | <Toggle field={form.$('automaticUpdates')} /> |
559 | <Button | 507 | {automaticUpdates && ( |
560 | label={intl.formatMessage(messages.buttonInstallUpdate)} | 508 | <div> |
561 | onClick={installUpdate} | 509 | <Toggle field={form.$('beta')} /> |
562 | /> | 510 | {updateIsReadyToInstall ? ( |
563 | ) : ( | 511 | <Button |
564 | <Button | 512 | label={intl.formatMessage(messages.buttonInstallUpdate)} |
565 | buttonType="secondary" | 513 | onClick={installUpdate} |
566 | label={intl.formatMessage(updateButtonLabelMessage)} | 514 | /> |
567 | onClick={checkForUpdates} | 515 | ) : ( |
568 | disabled={noUpdates || isCheckingForUpdates || isUpdateAvailable} | 516 | <Button |
569 | loaded={!isCheckingForUpdates || !isUpdateAvailable} | 517 | buttonType="secondary" |
570 | /> | 518 | label={intl.formatMessage(updateButtonLabelMessage)} |
519 | onClick={checkForUpdates} | ||
520 | disabled={!automaticUpdates || isCheckingForUpdates || isUpdateAvailable} | ||
521 | loaded={!isCheckingForUpdates || !isUpdateAvailable} | ||
522 | /> | ||
523 | )} | ||
524 | <br /> | ||
525 | </div> | ||
571 | )} | 526 | )} |
572 | <br /> | ||
573 | <Toggle field={form.$('beta')} /> | ||
574 | <Toggle field={form.$('noUpdates')} /> | ||
575 | {intl.formatMessage(messages.currentVersion)} | 527 | {intl.formatMessage(messages.currentVersion)} |
576 | {' '} | 528 | {' '} |
577 | {remote.app.getVersion()} | 529 | {remote.app.getVersion()} |
578 | <br /> | 530 | {noUpdateAvailable && ( |
579 | <br /> | 531 | <> |
580 | {noUpdateAvailable && intl.formatMessage(messages.updateStatusUpToDate)} | 532 | <br /> |
533 | <br /> | ||
534 | {intl.formatMessage(messages.updateStatusUpToDate)} | ||
535 | </> | ||
536 | ) | ||
537 | } | ||
581 | <p className="settings__message"> | 538 | <p className="settings__message"> |
582 | |||
583 | <span className="mdi mdi-github-face" /> | 539 | <span className="mdi mdi-github-face" /> |
584 | <span> | 540 | <span> |
541 | |||
585 | Ferdi is based on | 542 | Ferdi is based on |
586 | {' '} | 543 | {' '} |
587 | <a href="https://github.com/meetfranz/franz" target="_blank">Franz</a> | 544 | <a href="https://github.com/meetfranz/franz" target="_blank">Franz</a> |
545 | |||
588 | , a project published | 546 | , a project published |
589 | under the | 547 | under the |
590 | {' '} | 548 | {' '} |
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js index 3d5358d89..72358d485 100644 --- a/src/components/settings/team/TeamDashboard.js +++ b/src/components/settings/team/TeamDashboard.js | |||
@@ -20,15 +20,15 @@ const messages = defineMessages({ | |||
20 | }, | 20 | }, |
21 | contentHeadline: { | 21 | contentHeadline: { |
22 | id: 'settings.team.contentHeadline', | 22 | id: 'settings.team.contentHeadline', |
23 | defaultMessage: '!!!Ferdi for Teams', | 23 | defaultMessage: '!!!Franz Team Management', |
24 | }, | 24 | }, |
25 | intro: { | 25 | intro: { |
26 | id: 'settings.team.intro', | 26 | id: 'settings.team.intro', |
27 | defaultMessage: '!!!You and your team use Franz? You can now manage Premium subscriptions for as many colleagues, friends or family members as you want, all from within one account.', | 27 | defaultMessage: '!!!Your are currently using Franz Servers, which is why you have access to Team Management.', |
28 | }, | 28 | }, |
29 | copy: { | 29 | copy: { |
30 | id: 'settings.team.copy', | 30 | id: 'settings.team.copy', |
31 | defaultMessage: '!!!Ferdi for Teams gives you the option to invite co-workers to your team by sending them email invitations and manage their subscriptions in your account’s preferences. Don’t waste time setting up subscriptions for every team member individually, forget about multiple invoices and different billing cycles - one team to rule them all!', | 31 | defaultMessage: '!!!Franz\'s Team Management allows you to manage Franz Subscriptions for multiple users. Please keep in mind that having a Franz Premium subscription will give you no advantages in using Ferdi: The only reason you still have access to Team Management is so you can manage your legacy Franz Teams and so that you don\'t loose any functionality in managing your account.', |
32 | }, | 32 | }, |
33 | manageButton: { | 33 | manageButton: { |
34 | id: 'settings.team.manageAction', | 34 | id: 'settings.team.manageAction', |
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js index f1039709c..dbc2a9078 100644 --- a/src/components/ui/FeatureList.js +++ b/src/components/ui/FeatureList.js | |||
@@ -66,6 +66,10 @@ const messages = defineMessages({ | |||
66 | id: 'pricing.features.adFree', | 66 | id: 'pricing.features.adFree', |
67 | defaultMessage: '!!!Forever ad-free', | 67 | defaultMessage: '!!!Forever ad-free', |
68 | }, | 68 | }, |
69 | appDelayEnabled: { | ||
70 | id: 'pricing.features.appDelaysEnabled', | ||
71 | defaultMessage: '!!!Occasional Waiting Screens', | ||
72 | }, | ||
69 | }); | 73 | }); |
70 | 74 | ||
71 | export class FeatureList extends Component { | 75 | export class FeatureList extends Component { |
@@ -96,6 +100,7 @@ export class FeatureList extends Component { | |||
96 | const features = []; | 100 | const features = []; |
97 | if (plan === PLANS.FREE) { | 101 | if (plan === PLANS.FREE) { |
98 | features.push( | 102 | features.push( |
103 | messages.appDelayEnabled, | ||
99 | messages.upToThreeServices, | 104 | messages.upToThreeServices, |
100 | messages.availableRecipes, | 105 | messages.availableRecipes, |
101 | messages.accountSync, | 106 | messages.accountSync, |
diff --git a/src/components/ui/Slider.js b/src/components/ui/Slider.js new file mode 100644 index 000000000..b00a6a3f8 --- /dev/null +++ b/src/components/ui/Slider.js | |||
@@ -0,0 +1,65 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import classnames from 'classnames'; | ||
5 | import { Field } from 'mobx-react-form'; | ||
6 | |||
7 | export default @observer class Slider extends Component { | ||
8 | static propTypes = { | ||
9 | field: PropTypes.instanceOf(Field).isRequired, | ||
10 | className: PropTypes.string, | ||
11 | showLabel: PropTypes.bool, | ||
12 | disabled: PropTypes.bool, | ||
13 | }; | ||
14 | |||
15 | static defaultProps = { | ||
16 | className: '', | ||
17 | showLabel: true, | ||
18 | disabled: false, | ||
19 | }; | ||
20 | |||
21 | onChange(e) { | ||
22 | const { field } = this.props; | ||
23 | |||
24 | field.onChange(e); | ||
25 | } | ||
26 | |||
27 | render() { | ||
28 | const { | ||
29 | field, | ||
30 | className, | ||
31 | showLabel, | ||
32 | disabled, | ||
33 | } = this.props; | ||
34 | |||
35 | if (field.value === '' && field.default !== '') { | ||
36 | field.value = field.default; | ||
37 | } | ||
38 | |||
39 | return ( | ||
40 | <div | ||
41 | className={classnames([ | ||
42 | 'franz-form__field', | ||
43 | 'franz-form__slider-wrapper', | ||
44 | className, | ||
45 | ])} | ||
46 | > | ||
47 | <div className="slider-container"> | ||
48 | <input | ||
49 | className="slider" | ||
50 | type="range" | ||
51 | id={field.id} | ||
52 | name={field.name} | ||
53 | value={field.value} | ||
54 | min="1" | ||
55 | max="100" | ||
56 | onChange={e => (!disabled ? this.onChange(e) : null)} | ||
57 | /> | ||
58 | </div> | ||
59 | |||
60 | {field.error && <div className={field.error}>{field.error}</div>} | ||
61 | {field.label && showLabel && <label className="franz-form__label" htmlFor={field.id}>{field.label}</label>} | ||
62 | </div> | ||
63 | ); | ||
64 | } | ||
65 | } | ||