aboutsummaryrefslogtreecommitdiffstats
path: root/src/containers/settings
diff options
context:
space:
mode:
authorLibravatar Vijay A <vraravam@users.noreply.github.com>2021-10-06 22:59:03 +0530
committerLibravatar Vijay A <vraravam@users.noreply.github.com>2021-10-06 22:59:03 +0530
commit5fd7cd12cc62ceb6c4d654b3cb3b536412ed1216 (patch)
tree10cea6c608baea3481595ed9eb7be63ef03ca6c4 /src/containers/settings
parentBumped up version to: 5.6.2 (hotfix) (diff)
parent5.6.3-nightly.25 [skip ci] (diff)
downloadferdium-app-5fd7cd12cc62ceb6c4d654b3cb3b536412ed1216.tar.gz
ferdium-app-5fd7cd12cc62ceb6c4d654b3cb3b536412ed1216.tar.zst
ferdium-app-5fd7cd12cc62ceb6c4d654b3cb3b536412ed1216.zip
Merge branch 'nightly' into release
Diffstat (limited to 'src/containers/settings')
-rw-r--r--src/containers/settings/AccountScreen.js27
-rw-r--r--src/containers/settings/EditServiceScreen.js135
-rw-r--r--src/containers/settings/EditSettingsScreen.js179
-rw-r--r--src/containers/settings/EditUserScreen.js60
-rw-r--r--src/containers/settings/InviteScreen.js10
-rw-r--r--src/containers/settings/RecipesScreen.js95
-rw-r--r--src/containers/settings/ServicesScreen.js17
-rw-r--r--src/containers/settings/SettingsWindow.js25
-rw-r--r--src/containers/settings/SupportScreen.js9
-rw-r--r--src/containers/settings/TeamScreen.js11
10 files changed, 328 insertions, 240 deletions
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js
index 8d92b01be..1515fc22b 100644
--- a/src/containers/settings/AccountScreen.js
+++ b/src/containers/settings/AccountScreen.js
@@ -10,9 +10,8 @@ import SettingsStore from '../../stores/SettingsStore';
10import AccountDashboard from '../../components/settings/account/AccountDashboard'; 10import AccountDashboard from '../../components/settings/account/AccountDashboard';
11import ErrorBoundary from '../../components/util/ErrorBoundary'; 11import ErrorBoundary from '../../components/util/ErrorBoundary';
12import { LIVE_FRANZ_API } from '../../config'; 12import { LIVE_FRANZ_API } from '../../config';
13import { WEBSITE } from '../../environment'; 13import { WEBSITE } from '../../environment-remote';
14 14
15export default
16@inject('stores', 'actions') 15@inject('stores', 'actions')
17@observer 16@observer
18class AccountScreen extends Component { 17class AccountScreen extends Component {
@@ -33,14 +32,12 @@ class AccountScreen extends Component {
33 32
34 const api = stores.settings.all.app.server; 33 const api = stores.settings.all.app.server;
35 34
36 let url; 35 const url =
37 if (api === LIVE_FRANZ_API) { 36 api === LIVE_FRANZ_API
38 url = stores.user.getAuthURL( 37 ? stores.user.getAuthURL(
39 `${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`, 38 `${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`,
40 ); 39 )
41 } else { 40 : `${api}${route}`;
42 url = `${api}${route}`;
43 }
44 41
45 actions.app.openExternalUrl({ url }); 42 actions.app.openExternalUrl({ url });
46 } 43 }
@@ -58,16 +55,16 @@ class AccountScreen extends Component {
58 user={user.data} 55 user={user.data}
59 isLoading={isLoadingUserInfo} 56 isLoading={isLoadingUserInfo}
60 userInfoRequestFailed={ 57 userInfoRequestFailed={
61 user.getUserInfoRequest.wasExecuted 58 user.getUserInfoRequest.wasExecuted &&
62 && user.getUserInfoRequest.isError 59 user.getUserInfoRequest.isError
63 } 60 }
64 retryUserInfoRequest={() => this.reloadData()} 61 retryUserInfoRequest={() => this.reloadData()}
65 onCloseSubscriptionWindow={() => this.onCloseWindow()} 62 onCloseSubscriptionWindow={() => this.onCloseWindow()}
66 deleteAccount={userActions.delete} 63 deleteAccount={userActions.delete}
67 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting} 64 isLoadingDeleteAccount={user.deleteAccountRequest.isExecuting}
68 isDeleteAccountSuccessful={ 65 isDeleteAccountSuccessful={
69 user.deleteAccountRequest.wasExecuted 66 user.deleteAccountRequest.wasExecuted &&
70 && !user.deleteAccountRequest.isError 67 !user.deleteAccountRequest.isError
71 } 68 }
72 openEditAccount={() => this.handleWebsiteLink('/user/profile')} 69 openEditAccount={() => this.handleWebsiteLink('/user/profile')}
73 openInvoices={() => this.handleWebsiteLink('/user/invoices')} 70 openInvoices={() => this.handleWebsiteLink('/user/invoices')}
@@ -89,3 +86,5 @@ AccountScreen.wrappedComponent.propTypes = {
89 user: PropTypes.instanceOf(UserStore).isRequired, 86 user: PropTypes.instanceOf(UserStore).isRequired,
90 }).isRequired, 87 }).isRequired,
91}; 88};
89
90export default AccountScreen;
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js
index c880e97ae..dee7e7cff 100644
--- a/src/containers/settings/EditServiceScreen.js
+++ b/src/containers/settings/EditServiceScreen.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5 5
6import { RouterStore } from 'mobx-react-router'; 6import { RouterStore } from 'mobx-react-router';
7import UserStore from '../../stores/UserStore'; 7import UserStore from '../../stores/UserStore';
@@ -27,87 +27,89 @@ import globalMessages from '../../i18n/globalMessages';
27const messages = defineMessages({ 27const messages = defineMessages({
28 name: { 28 name: {
29 id: 'settings.service.form.name', 29 id: 'settings.service.form.name',
30 defaultMessage: '!!!Name', 30 defaultMessage: 'Name',
31 }, 31 },
32 enableService: { 32 enableService: {
33 id: 'settings.service.form.enableService', 33 id: 'settings.service.form.enableService',
34 defaultMessage: '!!!Enable service', 34 defaultMessage: 'Enable service',
35 }, 35 },
36 enableHibernation: { 36 enableHibernation: {
37 id: 'settings.service.form.enableHibernation', 37 id: 'settings.service.form.enableHibernation',
38 defaultMessage: '!!!Enable hibernation', 38 defaultMessage: 'Enable hibernation',
39 }, 39 },
40 enableNotification: { 40 enableNotification: {
41 id: 'settings.service.form.enableNotification', 41 id: 'settings.service.form.enableNotification',
42 defaultMessage: '!!!Enable Notifications', 42 defaultMessage: 'Enable notifications',
43 }, 43 },
44 enableBadge: { 44 enableBadge: {
45 id: 'settings.service.form.enableBadge', 45 id: 'settings.service.form.enableBadge',
46 defaultMessage: '!!!Show unread message badges', 46 defaultMessage: 'Show unread message badges',
47 }, 47 },
48 enableAudio: { 48 enableAudio: {
49 id: 'settings.service.form.enableAudio', 49 id: 'settings.service.form.enableAudio',
50 defaultMessage: '!!!Enable audio', 50 defaultMessage: 'Enable audio',
51 }, 51 },
52 team: { 52 team: {
53 id: 'settings.service.form.team', 53 id: 'settings.service.form.team',
54 defaultMessage: '!!!Team', 54 defaultMessage: 'Team',
55 }, 55 },
56 customUrl: { 56 customUrl: {
57 id: 'settings.service.form.customUrl', 57 id: 'settings.service.form.customUrl',
58 defaultMessage: '!!!Service URL', 58 defaultMessage: 'Custom server',
59 }, 59 },
60 indirectMessages: { 60 indirectMessages: {
61 id: 'settings.service.form.indirectMessages', 61 id: 'settings.service.form.indirectMessages',
62 defaultMessage: '!!!Show message badge for all new messages', 62 defaultMessage: 'Show message badge for all new messages',
63 }, 63 },
64 icon: { 64 icon: {
65 id: 'settings.service.form.icon', 65 id: 'settings.service.form.icon',
66 defaultMessage: '!!!Custom icon', 66 defaultMessage: 'Custom icon',
67 }, 67 },
68 enableDarkMode: { 68 enableDarkMode: {
69 id: 'settings.service.form.enableDarkMode', 69 id: 'settings.service.form.enableDarkMode',
70 defaultMessage: '!!!Enable Dark Mode', 70 defaultMessage: 'Enable Dark Mode',
71 }, 71 },
72 darkReaderBrightness: { 72 darkReaderBrightness: {
73 id: 'settings.service.form.darkReaderBrightness', 73 id: 'settings.service.form.darkReaderBrightness',
74 defaultMessage: '!!!Dark Reader Brightness', 74 defaultMessage: 'Dark Reader Brightness',
75 }, 75 },
76 darkReaderContrast: { 76 darkReaderContrast: {
77 id: 'settings.service.form.darkReaderContrast', 77 id: 'settings.service.form.darkReaderContrast',
78 defaultMessage: '!!!Dark Reader Contrast', 78 defaultMessage: 'Dark Reader Contrast',
79 }, 79 },
80 darkReaderSepia: { 80 darkReaderSepia: {
81 id: 'settings.service.form.darkReaderSepia', 81 id: 'settings.service.form.darkReaderSepia',
82 defaultMessage: '!!!Dark Reader Sepia', 82 defaultMessage: 'Dark Reader Sepia',
83 },
84 onlyShowFavoritesInUnreadCount: {
85 id: 'settings.service.form.onlyShowFavoritesInUnreadCount',
86 defaultMessage: 'Only show Favorites in unread count',
83 }, 87 },
84 enableProxy: { 88 enableProxy: {
85 id: 'settings.service.form.proxy.isEnabled', 89 id: 'settings.service.form.proxy.isEnabled',
86 defaultMessage: '!!!Use Proxy', 90 defaultMessage: 'Use Proxy',
87 }, 91 },
88 proxyHost: { 92 proxyHost: {
89 id: 'settings.service.form.proxy.host', 93 id: 'settings.service.form.proxy.host',
90 defaultMessage: '!!!Proxy Host/IP', 94 defaultMessage: 'Proxy Host/IP',
91 }, 95 },
92 proxyPort: { 96 proxyPort: {
93 id: 'settings.service.form.proxy.port', 97 id: 'settings.service.form.proxy.port',
94 defaultMessage: '!!!Port', 98 defaultMessage: 'Port',
95 }, 99 },
96 proxyUser: { 100 proxyUser: {
97 id: 'settings.service.form.proxy.user', 101 id: 'settings.service.form.proxy.user',
98 defaultMessage: '!!!User', 102 defaultMessage: 'User (optional)',
99 }, 103 },
100 proxyPassword: { 104 proxyPassword: {
101 id: 'settings.service.form.proxy.password', 105 id: 'settings.service.form.proxy.password',
102 defaultMessage: '!!!Password', 106 defaultMessage: 'Password (optional)',
103 }, 107 },
104}); 108});
105 109
106export default @inject('stores', 'actions') @observer class EditServiceScreen extends Component { 110@inject('stores', 'actions')
107 static contextTypes = { 111@observer
108 intl: intlShape, 112class EditServiceScreen extends Component {
109 };
110
111 onSubmit(data) { 113 onSubmit(data) {
112 const { action } = this.props.router.params; 114 const { action } = this.props.router.params;
113 const { recipes, services } = this.props.stores; 115 const { recipes, services } = this.props.stores;
@@ -132,27 +134,31 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
132 } 134 }
133 135
134 prepareForm(recipe, service, proxy) { 136 prepareForm(recipe, service, proxy) {
135 const { 137 const { intl } = this.props;
136 intl,
137 } = this.context;
138 138
139 const { 139 const { stores, router } = this.props;
140 stores,
141 router,
142 } = this.props;
143 140
144 const { action } = router.params; 141 const { action } = router.params;
145 142
146 let defaultSpellcheckerLanguage = SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage]; 143 let defaultSpellcheckerLanguage =
144 SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage];
147 145
148 if (stores.settings.app.spellcheckerLanguage === 'automatic') { 146 if (stores.settings.app.spellcheckerLanguage === 'automatic') {
149 defaultSpellcheckerLanguage = intl.formatMessage(globalMessages.spellcheckerAutomaticDetectionShort); 147 defaultSpellcheckerLanguage = intl.formatMessage(
148 globalMessages.spellcheckerAutomaticDetectionShort,
149 );
150 } 150 }
151 151
152 const spellcheckerLanguage = getSelectOptions({ 152 const spellcheckerLanguage = getSelectOptions({
153 locales: SPELLCHECKER_LOCALES, 153 locales: SPELLCHECKER_LOCALES,
154 resetToDefaultText: intl.formatMessage(globalMessages.spellcheckerSystemDefault, { default: defaultSpellcheckerLanguage }), 154 resetToDefaultText: intl.formatMessage(
155 automaticDetectionText: stores.settings.app.spellcheckerLanguage !== 'automatic' ? intl.formatMessage(globalMessages.spellcheckerAutomaticDetection) : '', 155 globalMessages.spellcheckerSystemDefault,
156 { default: defaultSpellcheckerLanguage },
157 ),
158 automaticDetectionText:
159 stores.settings.app.spellcheckerLanguage !== 'automatic'
160 ? intl.formatMessage(globalMessages.spellcheckerAutomaticDetection)
161 : '',
156 }); 162 });
157 163
158 const config = { 164 const config = {
@@ -169,7 +175,10 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
169 }, 175 },
170 isHibernationEnabled: { 176 isHibernationEnabled: {
171 label: intl.formatMessage(messages.enableHibernation), 177 label: intl.formatMessage(messages.enableHibernation),
172 value: action !== 'edit' ? recipe.autoHibernate : service.isHibernationEnabled, 178 value:
179 action !== 'edit'
180 ? recipe.autoHibernate
181 : service.isHibernationEnabled,
173 default: true, 182 default: true,
174 }, 183 },
175 isNotificationEnabled: { 184 isNotificationEnabled: {
@@ -200,17 +209,23 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
200 }, 209 },
201 darkReaderBrightness: { 210 darkReaderBrightness: {
202 label: intl.formatMessage(messages.darkReaderBrightness), 211 label: intl.formatMessage(messages.darkReaderBrightness),
203 value: service.darkReaderSettings ? service.darkReaderSettings.brightness : undefined, 212 value: service.darkReaderSettings
213 ? service.darkReaderSettings.brightness
214 : undefined,
204 default: 100, 215 default: 100,
205 }, 216 },
206 darkReaderContrast: { 217 darkReaderContrast: {
207 label: intl.formatMessage(messages.darkReaderContrast), 218 label: intl.formatMessage(messages.darkReaderContrast),
208 value: service.darkReaderSettings ? service.darkReaderSettings.contrast : undefined, 219 value: service.darkReaderSettings
220 ? service.darkReaderSettings.contrast
221 : undefined,
209 default: 90, 222 default: 90,
210 }, 223 },
211 darkReaderSepia: { 224 darkReaderSepia: {
212 label: intl.formatMessage(messages.darkReaderSepia), 225 label: intl.formatMessage(messages.darkReaderSepia),
213 value: service.darkReaderSettings ? service.darkReaderSettings.sepia : undefined, 226 value: service.darkReaderSettings
227 ? service.darkReaderSettings.sepia
228 : undefined,
214 default: 10, 229 default: 10,
215 }, 230 },
216 spellcheckerLanguage: { 231 spellcheckerLanguage: {
@@ -252,7 +267,10 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
252 // More fine grained and use case specific validation rules 267 // More fine grained and use case specific validation rules
253 if (recipe.hasTeamId && recipe.hasCustomUrl) { 268 if (recipe.hasTeamId && recipe.hasCustomUrl) {
254 config.fields.team.validators = [oneRequired(['team', 'customUrl'])]; 269 config.fields.team.validators = [oneRequired(['team', 'customUrl'])];
255 config.fields.customUrl.validators = [url, oneRequired(['team', 'customUrl'])]; 270 config.fields.customUrl.validators = [
271 url,
272 oneRequired(['team', 'customUrl']),
273 ];
256 } 274 }
257 275
258 // If a service can be hosted and has a teamId or customUrl 276 // If a service can be hosted and has a teamId or customUrl
@@ -275,6 +293,16 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
275 }); 293 });
276 } 294 }
277 295
296 if (recipe.allowFavoritesDelineationInUnreadCount) {
297 Object.assign(config.fields, {
298 onlyShowFavoritesInUnreadCount: {
299 label: intl.formatMessage(messages.onlyShowFavoritesInUnreadCount),
300 value: service.onlyShowFavoritesInUnreadCount,
301 default: false,
302 },
303 });
304 }
305
278 if (proxy.isEnabled) { 306 if (proxy.isEnabled) {
279 const serviceProxyConfig = stores.settings.proxy[service.id] || {}; 307 const serviceProxyConfig = stores.settings.proxy[service.id] || {};
280 308
@@ -344,9 +372,7 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
344 } 372 }
345 373
346 render() { 374 render() {
347 const { 375 const { recipes, services, user } = this.props.stores;
348 recipes, services, user,
349 } = this.props.stores;
350 const { action } = this.props.router.params; 376 const { action } = this.props.router.params;
351 377
352 let recipe; 378 let recipe;
@@ -358,9 +384,7 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
358 384
359 // TODO: render error message when recipe is `null` 385 // TODO: render error message when recipe is `null`
360 if (!recipe) { 386 if (!recipe) {
361 return ( 387 return <ServiceError />;
362 <ServiceError />
363 );
364 } 388 }
365 } else { 389 } else {
366 service = services.activeSettings; 390 service = services.activeSettings;
@@ -372,13 +396,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
372 } 396 }
373 397
374 if (isLoading) { 398 if (isLoading) {
375 return (<div>Loading...</div>); 399 return <div>Loading...</div>;
376 } 400 }
377 401
378 if (!recipe) { 402 if (!recipe) {
379 return ( 403 return <div>something went wrong</div>;
380 <div>something went wrong</div>
381 );
382 } 404 }
383 405
384 const form = this.prepareForm(recipe, service, proxyFeature); 406 const form = this.prepareForm(recipe, service, proxyFeature);
@@ -392,11 +414,14 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex
392 user={user.data} 414 user={user.data}
393 form={form} 415 form={form}
394 status={services.actionStatus} 416 status={services.actionStatus}
395 isSaving={services.updateServiceRequest.isExecuting || services.createServiceRequest.isExecuting} 417 isSaving={
418 services.updateServiceRequest.isExecuting ||
419 services.createServiceRequest.isExecuting
420 }
396 isDeleting={services.deleteServiceRequest.isExecuting} 421 isDeleting={services.deleteServiceRequest.isExecuting}
397 onSubmit={(d) => this.onSubmit(d)} 422 onSubmit={d => this.onSubmit(d)}
398 onDelete={() => this.deleteService()} 423 onDelete={() => this.deleteService()}
399 openRecipeFile={(file) => this.openRecipeFile(file)} 424 openRecipeFile={file => this.openRecipeFile(file)}
400 isProxyFeatureEnabled={proxyFeature.isEnabled} 425 isProxyFeatureEnabled={proxyFeature.isEnabled}
401 /> 426 />
402 </ErrorBoundary> 427 </ErrorBoundary>
@@ -417,3 +442,5 @@ EditServiceScreen.wrappedComponent.propTypes = {
417 service: PropTypes.instanceOf(ServicesStore).isRequired, 442 service: PropTypes.instanceOf(ServicesStore).isRequired,
418 }).isRequired, 443 }).isRequired,
419}; 444};
445
446export default injectIntl(EditServiceScreen);
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js
index 09244bc3c..1b05644f9 100644
--- a/src/containers/settings/EditSettingsScreen.js
+++ b/src/containers/settings/EditSettingsScreen.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5 5
6import AppStore from '../../stores/AppStore'; 6import AppStore from '../../stores/AppStore';
7import SettingsStore from '../../stores/SettingsStore'; 7import SettingsStore from '../../stores/SettingsStore';
@@ -10,10 +10,18 @@ import TodosStore from '../../features/todos/store';
10import Form from '../../lib/Form'; 10import Form from '../../lib/Form';
11import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; 11import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages';
12import { 12import {
13 HIBERNATION_STRATEGIES, SIDEBAR_WIDTH, ICON_SIZES, NAVIGATION_BAR_BEHAVIOURS, SEARCH_ENGINE_NAMES, TODO_APPS, 13 DEFAULT_APP_SETTINGS,
14 DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED, DEFAULT_IS_FEATURE_ENABLED_BY_USER, WAKE_UP_STRATEGIES, 14 HIBERNATION_STRATEGIES,
15 SIDEBAR_WIDTH,
16 ICON_SIZES,
17 NAVIGATION_BAR_BEHAVIOURS,
18 SEARCH_ENGINE_NAMES,
19 TODO_APPS,
20 DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED,
21 DEFAULT_IS_FEATURE_ENABLED_BY_USER,
22 WAKE_UP_STRATEGIES,
15} from '../../config'; 23} from '../../config';
16import { DEFAULT_APP_SETTINGS, isMac } from '../../environment'; 24import { isMac } from '../../environment';
17 25
18import { getSelectOptions } from '../../helpers/i18n-helpers'; 26import { getSelectOptions } from '../../helpers/i18n-helpers';
19import { hash } from '../../helpers/password-helpers'; 27import { hash } from '../../helpers/password-helpers';
@@ -31,195 +39,197 @@ const debug = require('debug')('Ferdi:EditSettingsScreen');
31const messages = defineMessages({ 39const messages = defineMessages({
32 autoLaunchOnStart: { 40 autoLaunchOnStart: {
33 id: 'settings.app.form.autoLaunchOnStart', 41 id: 'settings.app.form.autoLaunchOnStart',
34 defaultMessage: '!!!Launch Ferdi on start', 42 defaultMessage: 'Launch Ferdi on start',
35 }, 43 },
36 autoLaunchInBackground: { 44 autoLaunchInBackground: {
37 id: 'settings.app.form.autoLaunchInBackground', 45 id: 'settings.app.form.autoLaunchInBackground',
38 defaultMessage: '!!!Open in background', 46 defaultMessage: 'Open in background',
39 }, 47 },
40 runInBackground: { 48 runInBackground: {
41 id: 'settings.app.form.runInBackground', 49 id: 'settings.app.form.runInBackground',
42 defaultMessage: '!!!Keep Ferdi in background when closing the window', 50 defaultMessage: 'Keep Ferdi in background when closing the window',
43 }, 51 },
44 startMinimized: { 52 startMinimized: {
45 id: 'settings.app.form.startMinimized', 53 id: 'settings.app.form.startMinimized',
46 defaultMessage: '!!!Start minimized', 54 defaultMessage: 'Start minimized',
47 }, 55 },
48 confirmOnQuit: { 56 confirmOnQuit: {
49 id: 'settings.app.form.confirmOnQuit', 57 id: 'settings.app.form.confirmOnQuit',
50 defaultMessage: '!!!Confirm when quitting Ferdi', 58 defaultMessage: 'Confirm when quitting Ferdi',
51 }, 59 },
52 enableSystemTray: { 60 enableSystemTray: {
53 id: 'settings.app.form.enableSystemTray', 61 id: 'settings.app.form.enableSystemTray',
54 defaultMessage: '!!!Always show Ferdi in System Tray', 62 defaultMessage: 'Always show Ferdi in System Tray',
55 }, 63 },
56 enableMenuBar: { 64 enableMenuBar: {
57 id: 'settings.app.form.enableMenuBar', 65 id: 'settings.app.form.enableMenuBar',
58 defaultMessage: '!!!Always show Ferdi in Menu Bar', 66 defaultMessage: 'Always show Ferdi in Menu Bar',
59 }, 67 },
60 reloadAfterResume: { 68 reloadAfterResume: {
61 id: 'settings.app.form.reloadAfterResume', 69 id: 'settings.app.form.reloadAfterResume',
62 defaultMessage: '!!!Reload Ferdi after system resume', 70 defaultMessage: 'Reload Ferdi after system resume',
63 }, 71 },
64 minimizeToSystemTray: { 72 minimizeToSystemTray: {
65 id: 'settings.app.form.minimizeToSystemTray', 73 id: 'settings.app.form.minimizeToSystemTray',
66 defaultMessage: '!!!Minimize Ferdi to system tray', 74 defaultMessage: 'Minimize Ferdi to system tray',
67 }, 75 },
68 closeToSystemTray: { 76 closeToSystemTray: {
69 id: 'settings.app.form.closeToSystemTray', 77 id: 'settings.app.form.closeToSystemTray',
70 defaultMessage: '!!!Close Ferdi to system tray', 78 defaultMessage: 'Close Ferdi to system tray',
71 }, 79 },
72 privateNotifications: { 80 privateNotifications: {
73 id: 'settings.app.form.privateNotifications', 81 id: 'settings.app.form.privateNotifications',
74 defaultMessage: '!!!Don\'t show message content in notifications', 82 defaultMessage: "Don't show message content in notifications",
75 }, 83 },
76 clipboardNotifications: { 84 clipboardNotifications: {
77 id: 'settings.app.form.clipboardNotifications', 85 id: 'settings.app.form.clipboardNotifications',
78 defaultMessage: '!!!Don\'t show notifications for clipboard events', 86 defaultMessage: "Don't show notifications for clipboard events",
79 }, 87 },
80 notifyTaskBarOnMessage: { 88 notifyTaskBarOnMessage: {
81 id: 'settings.app.form.notifyTaskBarOnMessage', 89 id: 'settings.app.form.notifyTaskBarOnMessage',
82 defaultMessage: '!!!Notify TaskBar/Dock on new message', 90 defaultMessage: 'Notify TaskBar/Dock on new message',
83 }, 91 },
84 navigationBarBehaviour: { 92 navigationBarBehaviour: {
85 id: 'settings.app.form.navigationBarBehaviour', 93 id: 'settings.app.form.navigationBarBehaviour',
86 defaultMessage: '!!!Navigation bar behaviour', 94 defaultMessage: 'Navigation bar behaviour',
87 }, 95 },
88 searchEngine: { 96 searchEngine: {
89 id: 'settings.app.form.searchEngine', 97 id: 'settings.app.form.searchEngine',
90 defaultMessage: '!!!Search engine', 98 defaultMessage: 'Search engine',
91 }, 99 },
92 sentry: { 100 sentry: {
93 id: 'settings.app.form.sentry', 101 id: 'settings.app.form.sentry',
94 defaultMessage: '!!!Send telemetry data', 102 defaultMessage: 'Send telemetry data',
95 }, 103 },
96 hibernateOnStartup: { 104 hibernateOnStartup: {
97 id: 'settings.app.form.hibernateOnStartup', 105 id: 'settings.app.form.hibernateOnStartup',
98 defaultMessage: '!!!Keep services in hibernation on startup', 106 defaultMessage: 'Keep services in hibernation on startup',
99 }, 107 },
100 hibernationStrategy: { 108 hibernationStrategy: {
101 id: 'settings.app.form.hibernationStrategy', 109 id: 'settings.app.form.hibernationStrategy',
102 defaultMessage: '!!!Hibernation strategy', 110 defaultMessage: 'Hibernation strategy',
103 }, 111 },
104 wakeUpStrategy: { 112 wakeUpStrategy: {
105 id: 'settings.app.form.wakeUpStrategy', 113 id: 'settings.app.form.wakeUpStrategy',
106 defaultMessage: '!!!Wake up strategy', 114 defaultMessage: 'Wake up strategy',
107 }, 115 },
108 predefinedTodoServer: { 116 predefinedTodoServer: {
109 id: 'settings.app.form.predefinedTodoServer', 117 id: 'settings.app.form.predefinedTodoServer',
110 defaultMessage: '!!!Todo Server', 118 defaultMessage: 'Todo Server',
111 }, 119 },
112 customTodoServer: { 120 customTodoServer: {
113 id: 'settings.app.form.customTodoServer', 121 id: 'settings.app.form.customTodoServer',
114 defaultMessage: '!!!Custom TodoServer', 122 defaultMessage: 'Custom Todo Server',
115 }, 123 },
116 enableLock: { 124 enableLock: {
117 id: 'settings.app.form.enableLock', 125 id: 'settings.app.form.enableLock',
118 defaultMessage: '!!!Enable Password Lock', 126 defaultMessage: 'Enable Password Lock',
119 }, 127 },
120 lockPassword: { 128 lockPassword: {
121 id: 'settings.app.form.lockPassword', 129 id: 'settings.app.form.lockPassword',
122 defaultMessage: '!!!Password', 130 defaultMessage: 'Password',
123 }, 131 },
124 useTouchIdToUnlock: { 132 useTouchIdToUnlock: {
125 id: 'settings.app.form.useTouchIdToUnlock', 133 id: 'settings.app.form.useTouchIdToUnlock',
126 defaultMessage: '!!!Allow using Touch ID to unlock', 134 defaultMessage: 'Allow using TouchID to unlock Ferdi',
127 }, 135 },
128 inactivityLock: { 136 inactivityLock: {
129 id: 'settings.app.form.inactivityLock', 137 id: 'settings.app.form.inactivityLock',
130 defaultMessage: '!!!Lock after inactivity', 138 defaultMessage: 'Lock after inactivity',
131 }, 139 },
132 scheduledDNDEnabled: { 140 scheduledDNDEnabled: {
133 id: 'settings.app.form.scheduledDNDEnabled', 141 id: 'settings.app.form.scheduledDNDEnabled',
134 defaultMessage: '!!!Enable scheduled Do-not-Disturb', 142 defaultMessage: 'Enable scheduled Do-not-Disturb',
135 }, 143 },
136 scheduledDNDStart: { 144 scheduledDNDStart: {
137 id: 'settings.app.form.scheduledDNDStart', 145 id: 'settings.app.form.scheduledDNDStart',
138 defaultMessage: '!!!From', 146 defaultMessage: 'From',
139 }, 147 },
140 scheduledDNDEnd: { 148 scheduledDNDEnd: {
141 id: 'settings.app.form.scheduledDNDEnd', 149 id: 'settings.app.form.scheduledDNDEnd',
142 defaultMessage: '!!!To', 150 defaultMessage: 'To',
143 }, 151 },
144 language: { 152 language: {
145 id: 'settings.app.form.language', 153 id: 'settings.app.form.language',
146 defaultMessage: '!!!Language', 154 defaultMessage: 'Language',
147 }, 155 },
148 darkMode: { 156 darkMode: {
149 id: 'settings.app.form.darkMode', 157 id: 'settings.app.form.darkMode',
150 defaultMessage: '!!!Dark Mode', 158 defaultMessage: 'Enable Dark Mode',
151 }, 159 },
152 adaptableDarkMode: { 160 adaptableDarkMode: {
153 id: 'settings.app.form.adaptableDarkMode', 161 id: 'settings.app.form.adaptableDarkMode',
154 defaultMessage: '!!!Synchronize dark mode with my OS\'s dark mode setting', 162 defaultMessage: "Synchronize dark mode with my OS's dark mode setting",
155 }, 163 },
156 universalDarkMode: { 164 universalDarkMode: {
157 id: 'settings.app.form.universalDarkMode', 165 id: 'settings.app.form.universalDarkMode',
158 defaultMessage: '!!!Enable universal Dark Mode', 166 defaultMessage: 'Enable universal Dark Mode',
167 },
168 splitMode: {
169 id: 'settings.app.form.splitMode',
170 defaultMessage: 'Enable Split View Mode',
159 }, 171 },
160 serviceRibbonWidth: { 172 serviceRibbonWidth: {
161 id: 'settings.app.form.serviceRibbonWidth', 173 id: 'settings.app.form.serviceRibbonWidth',
162 defaultMessage: '!!!Sidebar width', 174 defaultMessage: 'Sidebar width',
163 }, 175 },
164 iconSize: { 176 iconSize: {
165 id: 'settings.app.form.iconSize', 177 id: 'settings.app.form.iconSize',
166 defaultMessage: '!!!Service icon size', 178 defaultMessage: 'Service icon size',
167 }, 179 },
168 useVerticalStyle: { 180 useVerticalStyle: {
169 id: 'settings.app.form.useVerticalStyle', 181 id: 'settings.app.form.useVerticalStyle',
170 defaultMessage: '!!!Use horizontal style', 182 defaultMessage: 'Use horizontal style',
171 }, 183 },
172 alwaysShowWorkspaces: { 184 alwaysShowWorkspaces: {
173 id: 'settings.app.form.alwaysShowWorkspaces', 185 id: 'settings.app.form.alwaysShowWorkspaces',
174 defaultMessage: '!!!Always show workspace drawer', 186 defaultMessage: 'Always show workspace drawer',
175 }, 187 },
176 accentColor: { 188 accentColor: {
177 id: 'settings.app.form.accentColor', 189 id: 'settings.app.form.accentColor',
178 defaultMessage: '!!!Accent color', 190 defaultMessage: 'Accent color',
179 }, 191 },
180 showDisabledServices: { 192 showDisabledServices: {
181 id: 'settings.app.form.showDisabledServices', 193 id: 'settings.app.form.showDisabledServices',
182 defaultMessage: '!!!Display disabled services tabs', 194 defaultMessage: 'Display disabled services tabs',
183 }, 195 },
184 showMessageBadgeWhenMuted: { 196 showMessageBadgeWhenMuted: {
185 id: 'settings.app.form.showMessagesBadgesWhenMuted', 197 id: 'settings.app.form.showMessagesBadgesWhenMuted',
186 defaultMessage: '!!!Show unread message badge when notifications are disabled', 198 defaultMessage: 'Show unread message badge when notifications are disabled',
187 }, 199 },
188 showDragArea: { 200 showDragArea: {
189 id: 'settings.app.form.showDragArea', 201 id: 'settings.app.form.showDragArea',
190 defaultMessage: '!!!Show draggable area on window', 202 defaultMessage: 'Show draggable area on window',
191 }, 203 },
192 enableSpellchecking: { 204 enableSpellchecking: {
193 id: 'settings.app.form.enableSpellchecking', 205 id: 'settings.app.form.enableSpellchecking',
194 defaultMessage: '!!!Enable spell checking', 206 defaultMessage: 'Enable spell checking',
195 }, 207 },
196 enableGPUAcceleration: { 208 enableGPUAcceleration: {
197 id: 'settings.app.form.enableGPUAcceleration', 209 id: 'settings.app.form.enableGPUAcceleration',
198 defaultMessage: '!!!Enable GPU Acceleration', 210 defaultMessage: 'Enable GPU Acceleration',
199 }, 211 },
200 beta: { 212 beta: {
201 id: 'settings.app.form.beta', 213 id: 'settings.app.form.beta',
202 defaultMessage: '!!!Include beta versions', 214 defaultMessage: 'Include beta versions',
203 }, 215 },
204 automaticUpdates: { 216 automaticUpdates: {
205 id: 'settings.app.form.automaticUpdates', 217 id: 'settings.app.form.automaticUpdates',
206 defaultMessage: '!!!Enable updates', 218 defaultMessage: 'Enable updates',
207 }, 219 },
208 enableTodos: { 220 enableTodos: {
209 id: 'settings.app.form.enableTodos', 221 id: 'settings.app.form.enableTodos',
210 defaultMessage: '!!!Enable Franz Todos', 222 defaultMessage: 'Enable Ferdi Todos',
211 }, 223 },
212 keepAllWorkspacesLoaded: { 224 keepAllWorkspacesLoaded: {
213 id: 'settings.app.form.keepAllWorkspacesLoaded', 225 id: 'settings.app.form.keepAllWorkspacesLoaded',
214 defaultMessage: '!!!Keep all workspaces loaded', 226 defaultMessage: 'Keep all workspaces loaded',
215 }, 227 },
216}); 228});
217 229
218export default @inject('stores', 'actions') @observer class EditSettingsScreen extends Component { 230@inject('stores', 'actions')
219 static contextTypes = { 231@observer
220 intl: intlShape, 232class EditSettingsScreen extends Component {
221 };
222
223 constructor(props) { 233 constructor(props) {
224 super(props); 234 super(props);
225 235
@@ -283,12 +293,15 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
283 darkMode: Boolean(settingsData.darkMode), 293 darkMode: Boolean(settingsData.darkMode),
284 adaptableDarkMode: Boolean(settingsData.adaptableDarkMode), 294 adaptableDarkMode: Boolean(settingsData.adaptableDarkMode),
285 universalDarkMode: Boolean(settingsData.universalDarkMode), 295 universalDarkMode: Boolean(settingsData.universalDarkMode),
296 splitMode: Boolean(settingsData.splitMode),
286 serviceRibbonWidth: Number(settingsData.serviceRibbonWidth), 297 serviceRibbonWidth: Number(settingsData.serviceRibbonWidth),
287 iconSize: Number(settingsData.iconSize), 298 iconSize: Number(settingsData.iconSize),
288 useVerticalStyle: Boolean(settingsData.useVerticalStyle), 299 useVerticalStyle: Boolean(settingsData.useVerticalStyle),
289 alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces), 300 alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces),
290 accentColor: settingsData.accentColor, 301 accentColor: settingsData.accentColor,
291 showMessageBadgeWhenMuted: Boolean(settingsData.showMessageBadgeWhenMuted), 302 showMessageBadgeWhenMuted: Boolean(
303 settingsData.showMessageBadgeWhenMuted,
304 ),
292 showDragArea: Boolean(settingsData.showDragArea), 305 showDragArea: Boolean(settingsData.showDragArea),
293 enableSpellchecking: Boolean(settingsData.enableSpellchecking), 306 enableSpellchecking: Boolean(settingsData.enableSpellchecking),
294 spellcheckerLanguage: settingsData.spellcheckerLanguage, 307 spellcheckerLanguage: settingsData.spellcheckerLanguage,
@@ -309,24 +322,27 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
309 322
310 if (workspaces.isFeatureActive) { 323 if (workspaces.isFeatureActive) {
311 const { keepAllWorkspacesLoaded } = workspaces.settings; 324 const { keepAllWorkspacesLoaded } = workspaces.settings;
312 if (Boolean(keepAllWorkspacesLoaded) !== Boolean(settingsData.keepAllWorkspacesLoaded)) { 325 if (
326 Boolean(keepAllWorkspacesLoaded) !==
327 Boolean(settingsData.keepAllWorkspacesLoaded)
328 ) {
313 workspaceActions.toggleKeepAllWorkspacesLoadedSetting(); 329 workspaceActions.toggleKeepAllWorkspacesLoadedSetting();
314 } 330 }
315 } 331 }
316 332
317 if (todos.isFeatureActive) { 333 if (todos.isFeatureActive) {
318 const { isFeatureEnabledByUser } = todos.settings; 334 const { isFeatureEnabledByUser } = todos.settings;
319 if (Boolean(isFeatureEnabledByUser) !== Boolean(settingsData.enableTodos)) { 335 if (
336 Boolean(isFeatureEnabledByUser) !== Boolean(settingsData.enableTodos)
337 ) {
320 todosActions.toggleTodosFeatureVisibility(); 338 todosActions.toggleTodosFeatureVisibility();
321 } 339 }
322 } 340 }
323 } 341 }
324 342
325 prepareForm() { 343 prepareForm() {
326 const { 344 const { app, settings, user, todos, workspaces } = this.props.stores;
327 app, settings, user, todos, workspaces, 345 const { intl } = this.props;
328 } = this.props.stores;
329 const { intl } = this.context;
330 const { lockedPassword } = this.state; 346 const { lockedPassword } = this.state;
331 347
332 const locales = getSelectOptions({ 348 const locales = getSelectOptions({
@@ -370,7 +386,9 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
370 386
371 const spellcheckingLanguages = getSelectOptions({ 387 const spellcheckingLanguages = getSelectOptions({
372 locales: SPELLCHECKER_LOCALES, 388 locales: SPELLCHECKER_LOCALES,
373 automaticDetectionText: this.context.intl.formatMessage(globalMessages.spellcheckerAutomaticDetection), 389 automaticDetectionText: intl.formatMessage(
390 globalMessages.spellcheckerAutomaticDetection,
391 ),
374 }); 392 });
375 393
376 const config = { 394 const config = {
@@ -401,7 +419,9 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
401 default: DEFAULT_APP_SETTINGS.confirmOnQuit, 419 default: DEFAULT_APP_SETTINGS.confirmOnQuit,
402 }, 420 },
403 enableSystemTray: { 421 enableSystemTray: {
404 label: intl.formatMessage(isMac ? messages.enableMenuBar : messages.enableSystemTray), 422 label: intl.formatMessage(
423 isMac ? messages.enableMenuBar : messages.enableSystemTray,
424 ),
405 value: settings.all.app.enableSystemTray, 425 value: settings.all.app.enableSystemTray,
406 default: DEFAULT_APP_SETTINGS.enableSystemTray, 426 default: DEFAULT_APP_SETTINGS.enableSystemTray,
407 }, 427 },
@@ -566,6 +586,11 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
566 value: settings.all.app.universalDarkMode, 586 value: settings.all.app.universalDarkMode,
567 default: DEFAULT_APP_SETTINGS.universalDarkMode, 587 default: DEFAULT_APP_SETTINGS.universalDarkMode,
568 }, 588 },
589 splitMode: {
590 label: intl.formatMessage(messages.splitMode),
591 value: settings.all.app.splitMode,
592 default: DEFAULT_APP_SETTINGS.splitMode,
593 },
569 serviceRibbonWidth: { 594 serviceRibbonWidth: {
570 label: intl.formatMessage(messages.serviceRibbonWidth), 595 label: intl.formatMessage(messages.serviceRibbonWidth),
571 value: settings.all.app.serviceRibbonWidth, 596 value: settings.all.app.serviceRibbonWidth,
@@ -637,23 +662,15 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
637 } 662 }
638 663
639 render() { 664 render() {
640 const { 665 const { app, todos, workspaces, services } = this.props.stores;
641 app,
642 todos,
643 workspaces,
644 services,
645 } = this.props.stores;
646 const { 666 const {
647 updateStatus, 667 updateStatus,
648 updateStatusTypes, 668 updateStatusTypes,
649 isClearingAllCache, 669 isClearingAllCache,
650 lockingFeatureEnabled, 670 lockingFeatureEnabled,
651 } = app; 671 } = app;
652 const { 672 const { checkForUpdates, installUpdate, clearAllCache } =
653 checkForUpdates, 673 this.props.actions.app;
654 installUpdate,
655 clearAllCache,
656 } = this.props.actions.app;
657 const form = this.prepareForm(); 674 const form = this.prepareForm();
658 675
659 return ( 676 return (
@@ -666,7 +683,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
666 isUpdateAvailable={updateStatus === updateStatusTypes.AVAILABLE} 683 isUpdateAvailable={updateStatus === updateStatusTypes.AVAILABLE}
667 noUpdateAvailable={updateStatus === updateStatusTypes.NOT_AVAILABLE} 684 noUpdateAvailable={updateStatus === updateStatusTypes.NOT_AVAILABLE}
668 updateIsReadyToInstall={updateStatus === updateStatusTypes.DOWNLOADED} 685 updateIsReadyToInstall={updateStatus === updateStatusTypes.DOWNLOADED}
669 onSubmit={(d) => this.onSubmit(d)} 686 onSubmit={d => this.onSubmit(d)}
670 getCacheSize={() => app.cacheSize} 687 getCacheSize={() => app.cacheSize}
671 isClearingAllCache={isClearingAllCache} 688 isClearingAllCache={isClearingAllCache}
672 onClearAllCache={clearAllCache} 689 onClearAllCache={clearAllCache}
@@ -675,9 +692,13 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e
675 lockingFeatureEnabled={lockingFeatureEnabled} 692 lockingFeatureEnabled={lockingFeatureEnabled}
676 automaticUpdates={this.props.stores.settings.app.automaticUpdates} 693 automaticUpdates={this.props.stores.settings.app.automaticUpdates}
677 isDarkmodeEnabled={this.props.stores.settings.app.darkMode} 694 isDarkmodeEnabled={this.props.stores.settings.app.darkMode}
678 isAdaptableDarkModeEnabled={this.props.stores.settings.app.adaptableDarkMode} 695 isAdaptableDarkModeEnabled={
696 this.props.stores.settings.app.adaptableDarkMode
697 }
679 isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser} 698 isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser}
680 isUsingCustomTodoService={this.props.stores.todos.isUsingCustomTodoService} 699 isUsingCustomTodoService={
700 this.props.stores.todos.isUsingCustomTodoService
701 }
681 isNightlyEnabled={this.props.stores.settings.app.nightly} 702 isNightlyEnabled={this.props.stores.settings.app.nightly}
682 hasAddedTodosAsService={services.isTodosServiceAdded} 703 hasAddedTodosAsService={services.isTodosServiceAdded}
683 isOnline={app.isOnline} 704 isOnline={app.isOnline}
@@ -704,3 +725,5 @@ EditSettingsScreen.wrappedComponent.propTypes = {
704 workspaces: PropTypes.instanceOf(WorkspacesStore).isRequired, 725 workspaces: PropTypes.instanceOf(WorkspacesStore).isRequired,
705 }).isRequired, 726 }).isRequired,
706}; 727};
728
729export default injectIntl(EditSettingsScreen);
diff --git a/src/containers/settings/EditUserScreen.js b/src/containers/settings/EditUserScreen.js
index 820b5e4d3..ca1363c59 100644
--- a/src/containers/settings/EditUserScreen.js
+++ b/src/containers/settings/EditUserScreen.js
@@ -1,7 +1,7 @@
1import React, { Component } from 'react'; 1import React, { Component } from 'react';
2import PropTypes from 'prop-types'; 2import PropTypes from 'prop-types';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4import { defineMessages, intlShape } from 'react-intl'; 4import { defineMessages, injectIntl } from 'react-intl';
5 5
6import UserStore from '../../stores/UserStore'; 6import UserStore from '../../stores/UserStore';
7import Form from '../../lib/Form'; 7import Form from '../../lib/Form';
@@ -13,47 +13,45 @@ import { required, email, minLength } from '../../helpers/validation-helpers';
13const messages = defineMessages({ 13const messages = defineMessages({
14 firstname: { 14 firstname: {
15 id: 'settings.user.form.firstname', 15 id: 'settings.user.form.firstname',
16 defaultMessage: '!!!Firstname', 16 defaultMessage: 'First Name',
17 }, 17 },
18 lastname: { 18 lastname: {
19 id: 'settings.user.form.lastname', 19 id: 'settings.user.form.lastname',
20 defaultMessage: '!!!Lastname', 20 defaultMessage: 'Last Name',
21 }, 21 },
22 email: { 22 email: {
23 id: 'settings.user.form.email', 23 id: 'settings.user.form.email',
24 defaultMessage: '!!!Email', 24 defaultMessage: 'Email',
25 }, 25 },
26 accountTypeLabel: { 26 accountTypeLabel: {
27 id: 'settings.user.form.accountType.label', 27 id: 'settings.user.form.accountType.label',
28 defaultMessage: '!!!Account type', 28 defaultMessage: 'Account type',
29 }, 29 },
30 accountTypeIndividual: { 30 accountTypeIndividual: {
31 id: 'settings.user.form.accountType.individual', 31 id: 'settings.user.form.accountType.individual',
32 defaultMessage: '!!!Individual', 32 defaultMessage: 'Individual',
33 }, 33 },
34 accountTypeNonProfit: { 34 accountTypeNonProfit: {
35 id: 'settings.user.form.accountType.non-profit', 35 id: 'settings.user.form.accountType.non-profit',
36 defaultMessage: '!!!Non-Profit', 36 defaultMessage: 'Non-Profit',
37 }, 37 },
38 accountTypeCompany: { 38 accountTypeCompany: {
39 id: 'settings.user.form.accountType.company', 39 id: 'settings.user.form.accountType.company',
40 defaultMessage: '!!!Company', 40 defaultMessage: 'Company',
41 }, 41 },
42 currentPassword: { 42 currentPassword: {
43 id: 'settings.user.form.currentPassword', 43 id: 'settings.user.form.currentPassword',
44 defaultMessage: '!!!Current password', 44 defaultMessage: 'Current password',
45 }, 45 },
46 newPassword: { 46 newPassword: {
47 id: 'settings.user.form.newPassword', 47 id: 'settings.user.form.newPassword',
48 defaultMessage: '!!!New password', 48 defaultMessage: 'New password',
49 }, 49 },
50}); 50});
51 51
52export default @inject('stores', 'actions') @observer class EditUserScreen extends Component { 52@inject('stores', 'actions')
53 static contextTypes = { 53@observer
54 intl: intlShape, 54class EditUserScreen extends Component {
55 };
56
57 componentWillUnmount() { 55 componentWillUnmount() {
58 this.props.actions.user.resetStatus(); 56 this.props.actions.user.resetStatus();
59 } 57 }
@@ -67,7 +65,7 @@ export default @inject('stores', 'actions') @observer class EditUserScreen exten
67 } 65 }
68 66
69 prepareForm(user) { 67 prepareForm(user) {
70 const { intl } = this.context; 68 const { intl } = this.props;
71 69
72 const config = { 70 const config = {
73 fields: { 71 fields: {
@@ -93,16 +91,20 @@ export default @inject('stores', 'actions') @observer class EditUserScreen exten
93 value: user.accountType, 91 value: user.accountType,
94 validators: [required], 92 validators: [required],
95 label: intl.formatMessage(messages.accountTypeLabel), 93 label: intl.formatMessage(messages.accountTypeLabel),
96 options: [{ 94 options: [
97 value: 'individual', 95 {
98 label: intl.formatMessage(messages.accountTypeIndividual), 96 value: 'individual',
99 }, { 97 label: intl.formatMessage(messages.accountTypeIndividual),
100 value: 'non-profit', 98 },
101 label: intl.formatMessage(messages.accountTypeNonProfit), 99 {
102 }, { 100 value: 'non-profit',
103 value: 'company', 101 label: intl.formatMessage(messages.accountTypeNonProfit),
104 label: intl.formatMessage(messages.accountTypeCompany), 102 },
105 }], 103 {
104 value: 'company',
105 label: intl.formatMessage(messages.accountTypeCompany),
106 },
107 ],
106 }, 108 },
107 organization: { 109 organization: {
108 label: intl.formatMessage(messages.accountTypeCompany), 110 label: intl.formatMessage(messages.accountTypeCompany),
@@ -129,7 +131,7 @@ export default @inject('stores', 'actions') @observer class EditUserScreen exten
129 const { user } = this.props.stores; 131 const { user } = this.props.stores;
130 132
131 if (user.getUserInfoRequest.isExecuting) { 133 if (user.getUserInfoRequest.isExecuting) {
132 return (<div>Loading...</div>); 134 return <div>Loading...</div>;
133 } 135 }
134 136
135 const form = this.prepareForm(user.data); 137 const form = this.prepareForm(user.data);
@@ -141,7 +143,7 @@ export default @inject('stores', 'actions') @observer class EditUserScreen exten
141 status={user.actionStatus} 143 status={user.actionStatus}
142 form={form} 144 form={form}
143 isSaving={user.updateUserInfoRequest.isExecuting} 145 isSaving={user.updateUserInfoRequest.isExecuting}
144 onSubmit={(d) => this.onSubmit(d)} 146 onSubmit={d => this.onSubmit(d)}
145 /> 147 />
146 </ErrorBoundary> 148 </ErrorBoundary>
147 ); 149 );
@@ -156,3 +158,5 @@ EditUserScreen.wrappedComponent.propTypes = {
156 user: PropTypes.instanceOf(UserStore).isRequired, 158 user: PropTypes.instanceOf(UserStore).isRequired,
157 }).isRequired, 159 }).isRequired,
158}; 160};
161
162export default injectIntl(EditUserScreen);
diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js
index 4fdaef08b..bf393f42f 100644
--- a/src/containers/settings/InviteScreen.js
+++ b/src/containers/settings/InviteScreen.js
@@ -6,7 +6,9 @@ import Invite from '../../components/auth/Invite';
6import ErrorBoundary from '../../components/util/ErrorBoundary'; 6import ErrorBoundary from '../../components/util/ErrorBoundary';
7import UserStore from '../../stores/UserStore'; 7import UserStore from '../../stores/UserStore';
8 8
9export default @inject('stores', 'actions') @observer class InviteScreen extends Component { 9@inject('stores', 'actions')
10@observer
11class InviteScreen extends Component {
10 componentWillUnmount() { 12 componentWillUnmount() {
11 this.props.stores.user.inviteRequest.reset(); 13 this.props.stores.user.inviteRequest.reset();
12 } 14 }
@@ -20,7 +22,9 @@ export default @inject('stores', 'actions') @observer class InviteScreen extends
20 <Invite 22 <Invite
21 onSubmit={actions.user.invite} 23 onSubmit={actions.user.invite}
22 isLoadingInvite={user.inviteRequest.isExecuting} 24 isLoadingInvite={user.inviteRequest.isExecuting}
23 isInviteSuccessful={user.inviteRequest.wasExecuted && !user.inviteRequest.isError} 25 isInviteSuccessful={
26 user.inviteRequest.wasExecuted && !user.inviteRequest.isError
27 }
24 embed 28 embed
25 /> 29 />
26 </ErrorBoundary> 30 </ErrorBoundary>
@@ -36,3 +40,5 @@ InviteScreen.wrappedComponent.propTypes = {
36 user: PropTypes.instanceOf(UserStore).isRequired, 40 user: PropTypes.instanceOf(UserStore).isRequired,
37 }).isRequired, 41 }).isRequired,
38}; 42};
43
44export default InviteScreen;
diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js
index 784052bbe..2e60ceefa 100644
--- a/src/containers/settings/RecipesScreen.js
+++ b/src/containers/settings/RecipesScreen.js
@@ -12,13 +12,16 @@ import UserStore from '../../stores/UserStore';
12import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; 12import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard';
13import ErrorBoundary from '../../components/util/ErrorBoundary'; 13import ErrorBoundary from '../../components/util/ErrorBoundary';
14import { CUSTOM_WEBSITE_RECIPE_ID, FRANZ_DEV_DOCS } from '../../config'; 14import { CUSTOM_WEBSITE_RECIPE_ID, FRANZ_DEV_DOCS } from '../../config';
15import { asarRecipesPath, userDataRecipesPath } from '../../environment'; 15import { userDataRecipesPath } from '../../environment-remote';
16import { asarRecipesPath } from '../../helpers/asar-helpers';
16import { communityRecipesStore } from '../../features/communityRecipes'; 17import { communityRecipesStore } from '../../features/communityRecipes';
17import RecipePreview from '../../models/RecipePreview'; 18import RecipePreview from '../../models/RecipePreview';
18import AppStore from '../../stores/AppStore'; 19import AppStore from '../../stores/AppStore';
19import { openPath } from '../../helpers/url-helpers'; 20import { openPath } from '../../helpers/url-helpers';
20 21
21export default @inject('stores', 'actions') @observer class RecipesScreen extends Component { 22@inject('stores', 'actions')
23@observer
24class RecipesScreen extends Component {
22 static propTypes = { 25 static propTypes = {
23 params: PropTypes.shape({ 26 params: PropTypes.shape({
24 filter: PropTypes.string, 27 filter: PropTypes.string,
@@ -75,25 +78,32 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
75 } 78 }
76 79
77 _sortByName(recipe1, recipe2) { 80 _sortByName(recipe1, recipe2) {
78 if (recipe1.name.toLowerCase() < recipe2.name.toLowerCase()) { return -1; } 81 if (recipe1.name.toLowerCase() < recipe2.name.toLowerCase()) {
79 if (recipe1.name.toLowerCase() > recipe2.name.toLowerCase()) { return 1; } 82 return -1;
83 }
84 if (recipe1.name.toLowerCase() > recipe2.name.toLowerCase()) {
85 return 1;
86 }
80 return 0; 87 return 0;
81 } 88 }
82 89
83 prepareRecipes(recipes) { 90 prepareRecipes(recipes) {
84 return recipes 91 return (
85 // Filter out duplicate recipes 92 recipes
86 .filter((recipe, index, self) => { 93 // Filter out duplicate recipes
87 const ids = self.map((rec) => rec.id); 94 .filter((recipe, index, self) => {
88 return ids.indexOf(recipe.id) === index; 95 const ids = self.map(rec => rec.id);
89 96 return ids.indexOf(recipe.id) === index;
90 // Sort alphabetically 97
91 }).sort(this._sortByName); 98 // Sort alphabetically
99 })
100 .sort(this._sortByName)
101 );
92 } 102 }
93 103
94 // Create an array of RecipePreviews from an array of recipe objects 104 // Create an array of RecipePreviews from an array of recipe objects
95 createPreviews(recipes) { 105 createPreviews(recipes) {
96 return recipes.map((recipe) => new RecipePreview(recipe)); 106 return recipes.map(recipe => new RecipePreview(recipe));
97 } 107 }
98 108
99 resetSearch() { 109 resetSearch() {
@@ -101,16 +111,9 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
101 } 111 }
102 112
103 render() { 113 render() {
104 const { 114 const { recipePreviews, recipes, services } = this.props.stores;
105 recipePreviews,
106 recipes,
107 services,
108 } = this.props.stores;
109 115
110 const { 116 const { app: appActions, service: serviceActions } = this.props.actions;
111 app: appActions,
112 service: serviceActions,
113 } = this.props.actions;
114 117
115 const { filter } = { filter: 'all', ...this.props.params }; 118 const { filter } = { filter: 'all', ...this.props.params };
116 let recipeFilter; 119 let recipeFilter;
@@ -125,21 +128,33 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
125 } 128 }
126 recipeFilter = recipeFilter.sort(this._sortByName); 129 recipeFilter = recipeFilter.sort(this._sortByName);
127 130
128 const allRecipes = this.state.needle ? this.prepareRecipes([ 131 const allRecipes = this.state.needle
129 // All search recipes from server 132 ? this.prepareRecipes([
130 ...recipePreviews.searchResults, 133 // All search recipes from server
131 // All search recipes from local recipes 134 ...recipePreviews.searchResults,
132 ...this.createPreviews( 135 // All search recipes from local recipes
133 this.customRecipes 136 ...this.createPreviews(
134 .filter((service) => service.name.toLowerCase().includes(this.state.needle.toLowerCase()) || (service.aliases || []).some(alias => alias.toLowerCase().includes(this.state.needle.toLowerCase()))), 137 this.customRecipes.filter(
135 ), 138 service =>
136 ]).sort(this._sortByName) : recipeFilter; 139 service.name
137 140 .toLowerCase()
138 const customWebsiteRecipe = recipePreviews.all.find((service) => service.id === CUSTOM_WEBSITE_RECIPE_ID); 141 .includes(this.state.needle.toLowerCase()) ||
142 (service.aliases || []).some(alias =>
143 alias.toLowerCase().includes(this.state.needle.toLowerCase()),
144 ),
145 ),
146 ),
147 ]).sort(this._sortByName)
148 : recipeFilter;
149
150 const customWebsiteRecipe = recipePreviews.all.find(
151 service => service.id === CUSTOM_WEBSITE_RECIPE_ID,
152 );
139 153
140 const isLoading = recipePreviews.allRecipePreviewsRequest.isExecuting 154 const isLoading =
141 || recipes.installRecipeRequest.isExecuting 155 recipePreviews.allRecipePreviewsRequest.isExecuting ||
142 || recipePreviews.searchRecipePreviewsRequest.isExecuting; 156 recipes.installRecipeRequest.isExecuting ||
157 recipePreviews.searchRecipePreviewsRequest.isExecuting;
143 158
144 const recipeDirectory = userDataRecipesPath('dev'); 159 const recipeDirectory = userDataRecipesPath('dev');
145 160
@@ -151,14 +166,16 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend
151 isLoading={isLoading} 166 isLoading={isLoading}
152 addedServiceCount={services.all.length} 167 addedServiceCount={services.all.length}
153 showAddServiceInterface={serviceActions.showAddServiceInterface} 168 showAddServiceInterface={serviceActions.showAddServiceInterface}
154 searchRecipes={(e) => this.searchRecipes(e)} 169 searchRecipes={e => this.searchRecipes(e)}
155 resetSearch={() => this.resetSearch()} 170 resetSearch={() => this.resetSearch()}
156 searchNeedle={this.state.needle} 171 searchNeedle={this.state.needle}
157 serviceStatus={services.actionStatus} 172 serviceStatus={services.actionStatus}
158 recipeFilter={filter} 173 recipeFilter={filter}
159 recipeDirectory={recipeDirectory} 174 recipeDirectory={recipeDirectory}
160 openRecipeDirectory={() => openPath(recipeDirectory)} 175 openRecipeDirectory={() => openPath(recipeDirectory)}
161 openDevDocs={() => appActions.openExternalUrl({ url: FRANZ_DEV_DOCS })} 176 openDevDocs={() =>
177 appActions.openExternalUrl({ url: FRANZ_DEV_DOCS })
178 }
162 /> 179 />
163 </ErrorBoundary> 180 </ErrorBoundary>
164 ); 181 );
@@ -180,3 +197,5 @@ RecipesScreen.wrappedComponent.propTypes = {
180 }).isRequired, 197 }).isRequired,
181 }).isRequired, 198 }).isRequired,
182}; 199};
200
201export default RecipesScreen;
diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js
index eb2b1bcb5..c9dfc68d0 100644
--- a/src/containers/settings/ServicesScreen.js
+++ b/src/containers/settings/ServicesScreen.js
@@ -10,7 +10,9 @@ import ServiceStore from '../../stores/ServicesStore';
10import ServicesDashboard from '../../components/settings/services/ServicesDashboard'; 10import ServicesDashboard from '../../components/settings/services/ServicesDashboard';
11import ErrorBoundary from '../../components/util/ErrorBoundary'; 11import ErrorBoundary from '../../components/util/ErrorBoundary';
12 12
13export default @inject('stores', 'actions') @observer class ServicesScreen extends Component { 13@inject('stores', 'actions')
14@observer
15class ServicesScreen extends Component {
14 componentWillUnmount() { 16 componentWillUnmount() {
15 this.props.actions.service.resetFilter(); 17 this.props.actions.service.resetFilter();
16 this.props.actions.service.resetStatus(); 18 this.props.actions.service.resetStatus();
@@ -23,11 +25,7 @@ export default @inject('stores', 'actions') @observer class ServicesScreen exten
23 25
24 render() { 26 render() {
25 const { user, services, router } = this.props.stores; 27 const { user, services, router } = this.props.stores;
26 const { 28 const { toggleService, filter, resetFilter } = this.props.actions.service;
27 toggleService,
28 filter,
29 resetFilter,
30 } = this.props.actions.service;
31 const isLoading = services.allServicesRequest.isExecuting; 29 const isLoading = services.allServicesRequest.isExecuting;
32 30
33 let allServices = services.all; 31 let allServices = services.all;
@@ -47,7 +45,10 @@ export default @inject('stores', 'actions') @observer class ServicesScreen exten
47 filterServices={filter} 45 filterServices={filter}
48 resetFilter={resetFilter} 46 resetFilter={resetFilter}
49 goTo={router.push} 47 goTo={router.push}
50 servicesRequestFailed={services.allServicesRequest.wasExecuted && services.allServicesRequest.isError} 48 servicesRequestFailed={
49 services.allServicesRequest.wasExecuted &&
50 services.allServicesRequest.isError
51 }
51 retryServicesRequest={() => services.allServicesRequest.reload()} 52 retryServicesRequest={() => services.allServicesRequest.reload()}
52 searchNeedle={services.filterNeedle} 53 searchNeedle={services.filterNeedle}
53 /> 54 />
@@ -66,3 +67,5 @@ ServicesScreen.wrappedComponent.propTypes = {
66 service: PropTypes.instanceOf(ServiceStore).isRequired, 67 service: PropTypes.instanceOf(ServiceStore).isRequired,
67 }).isRequired, 68 }).isRequired,
68}; 69};
70
71export default ServicesScreen;
diff --git a/src/containers/settings/SettingsWindow.js b/src/containers/settings/SettingsWindow.js
index 9bb64b6fe..e03c4c1d2 100644
--- a/src/containers/settings/SettingsWindow.js
+++ b/src/containers/settings/SettingsWindow.js
@@ -11,17 +11,19 @@ import ErrorBoundary from '../../components/util/ErrorBoundary';
11import { workspaceStore } from '../../features/workspaces'; 11import { workspaceStore } from '../../features/workspaces';
12import UIStore from '../../stores/UIStore'; 12import UIStore from '../../stores/UIStore';
13 13
14export default @inject('stores', 'actions') @observer class SettingsContainer extends Component { 14@inject('stores', 'actions')
15@observer
16class SettingsContainer extends Component {
15 portalRoot = document.querySelector('#portalContainer'); 17 portalRoot = document.querySelector('#portalContainer');
16 18
17 el = document.createElement('div'); 19 el = document.createElement('div');
18 20
19 componentDidMount() { 21 componentDidMount() {
20 this.portalRoot.appendChild(this.el); 22 this.portalRoot.append(this.el);
21 } 23 }
22 24
23 componentWillUnmount() { 25 componentWillUnmount() {
24 this.portalRoot.removeChild(this.el); 26 this.el.remove();
25 } 27 }
26 28
27 render() { 29 render() {
@@ -36,16 +38,11 @@ export default @inject('stores', 'actions') @observer class SettingsContainer ex
36 ); 38 );
37 39
38 return ReactDOM.createPortal( 40 return ReactDOM.createPortal(
39 ( 41 <ErrorBoundary>
40 <ErrorBoundary> 42 <Layout navigation={navigation} closeSettings={closeSettings}>
41 <Layout 43 {children}
42 navigation={navigation} 44 </Layout>
43 closeSettings={closeSettings} 45 </ErrorBoundary>,
44 >
45 {children}
46 </Layout>
47 </ErrorBoundary>
48 ),
49 this.el, 46 this.el,
50 ); 47 );
51 } 48 }
@@ -60,3 +57,5 @@ SettingsContainer.wrappedComponent.propTypes = {
60 ui: PropTypes.instanceOf(UIStore).isRequired, 57 ui: PropTypes.instanceOf(UIStore).isRequired,
61 }).isRequired, 58 }).isRequired,
62}; 59};
60
61export default SettingsContainer;
diff --git a/src/containers/settings/SupportScreen.js b/src/containers/settings/SupportScreen.js
index 7d3b22f19..646f672ce 100644
--- a/src/containers/settings/SupportScreen.js
+++ b/src/containers/settings/SupportScreen.js
@@ -6,7 +6,8 @@ import SupportFerdi from '../../components/settings/supportFerdi/SupportFerdiDas
6import ErrorBoundary from '../../components/util/ErrorBoundary'; 6import ErrorBoundary from '../../components/util/ErrorBoundary';
7import AppStore from '../../stores/AppStore'; 7import AppStore from '../../stores/AppStore';
8 8
9export default @inject('actions') class SupportScreen extends Component { 9@inject('actions')
10class SupportScreen extends Component {
10 constructor(props) { 11 constructor(props) {
11 super(props); 12 super(props);
12 13
@@ -20,9 +21,7 @@ export default @inject('actions') class SupportScreen extends Component {
20 render() { 21 render() {
21 return ( 22 return (
22 <ErrorBoundary> 23 <ErrorBoundary>
23 <SupportFerdi 24 <SupportFerdi openLink={this.openLink} />
24 openLink={this.openLink}
25 />
26 </ErrorBoundary> 25 </ErrorBoundary>
27 ); 26 );
28 } 27 }
@@ -33,3 +32,5 @@ SupportScreen.wrappedComponent.propTypes = {
33 app: PropTypes.instanceOf(AppStore).isRequired, 32 app: PropTypes.instanceOf(AppStore).isRequired,
34 }).isRequired, 33 }).isRequired,
35}; 34};
35
36export default SupportScreen;
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js
index 10c2e36ef..ea447469b 100644
--- a/src/containers/settings/TeamScreen.js
+++ b/src/containers/settings/TeamScreen.js
@@ -10,7 +10,9 @@ import TeamDashboard from '../../components/settings/team/TeamDashboard';
10import ErrorBoundary from '../../components/util/ErrorBoundary'; 10import ErrorBoundary from '../../components/util/ErrorBoundary';
11import { DEV_API_FRANZ_WEBSITE } from '../../config'; 11import { DEV_API_FRANZ_WEBSITE } from '../../config';
12 12
13export default @inject('stores', 'actions') @observer class TeamScreen extends Component { 13@inject('stores', 'actions')
14@observer
15class TeamScreen extends Component {
14 handleWebsiteLink(route) { 16 handleWebsiteLink(route) {
15 const { actions, stores } = this.props; 17 const { actions, stores } = this.props;
16 18
@@ -29,7 +31,10 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C
29 <ErrorBoundary> 31 <ErrorBoundary>
30 <TeamDashboard 32 <TeamDashboard
31 isLoading={isLoadingUserInfo} 33 isLoading={isLoadingUserInfo}
32 userInfoRequestFailed={user.getUserInfoRequest.wasExecuted && user.getUserInfoRequest.isError} 34 userInfoRequestFailed={
35 user.getUserInfoRequest.wasExecuted &&
36 user.getUserInfoRequest.isError
37 }
33 retryUserInfoRequest={() => this.reloadData()} 38 retryUserInfoRequest={() => this.reloadData()}
34 openTeamManagement={() => this.handleWebsiteLink('/user/team')} 39 openTeamManagement={() => this.handleWebsiteLink('/user/team')}
35 server={server} 40 server={server}
@@ -50,3 +55,5 @@ TeamScreen.wrappedComponent.propTypes = {
50 user: PropTypes.instanceOf(UserStore).isRequired, 55 user: PropTypes.instanceOf(UserStore).isRequired,
51 }).isRequired, 56 }).isRequired,
52}; 57};
58
59export default TeamScreen;