diff options
Diffstat (limited to 'src/containers')
-rw-r--r-- | src/containers/auth/AuthLayoutContainer.js | 13 | ||||
-rw-r--r-- | src/containers/auth/LockedScreen.js | 72 | ||||
-rw-r--r-- | src/containers/layout/AppLayoutContainer.js | 1 | ||||
-rw-r--r-- | src/containers/settings/AccountScreen.js | 9 | ||||
-rw-r--r-- | src/containers/settings/EditServiceScreen.js | 29 | ||||
-rw-r--r-- | src/containers/settings/EditSettingsScreen.js | 174 | ||||
-rw-r--r-- | src/containers/settings/RecipesScreen.js | 51 | ||||
-rw-r--r-- | src/containers/settings/SupportScreen.js | 36 | ||||
-rw-r--r-- | src/containers/settings/TeamScreen.js | 6 |
9 files changed, 377 insertions, 14 deletions
diff --git a/src/containers/auth/AuthLayoutContainer.js b/src/containers/auth/AuthLayoutContainer.js index 427054d3d..883a6ea60 100644 --- a/src/containers/auth/AuthLayoutContainer.js +++ b/src/containers/auth/AuthLayoutContainer.js | |||
@@ -5,6 +5,7 @@ import { ThemeProvider } from 'react-jss'; | |||
5 | 5 | ||
6 | import AuthLayout from '../../components/auth/AuthLayout'; | 6 | import AuthLayout from '../../components/auth/AuthLayout'; |
7 | import AppStore from '../../stores/AppStore'; | 7 | import AppStore from '../../stores/AppStore'; |
8 | import UserStore from '../../stores/UserStore'; | ||
8 | import GlobalErrorStore from '../../stores/GlobalErrorStore'; | 9 | import GlobalErrorStore from '../../stores/GlobalErrorStore'; |
9 | import AppLoader from '../../components/ui/AppLoader'; | 10 | import AppLoader from '../../components/ui/AppLoader'; |
10 | 11 | ||
@@ -23,7 +24,7 @@ export default @inject('stores', 'actions') @observer class AuthLayoutContainer | |||
23 | stores, actions, children, location, | 24 | stores, actions, children, location, |
24 | } = this.props; | 25 | } = this.props; |
25 | const { | 26 | const { |
26 | app, features, globalError, | 27 | app, features, globalError, user, |
27 | } = stores; | 28 | } = stores; |
28 | 29 | ||
29 | const isLoadingBaseFeatures = features.defaultFeaturesRequest.isExecuting | 30 | const isLoadingBaseFeatures = features.defaultFeaturesRequest.isExecuting |
@@ -37,6 +38,15 @@ export default @inject('stores', 'actions') @observer class AuthLayoutContainer | |||
37 | ); | 38 | ); |
38 | } | 39 | } |
39 | 40 | ||
41 | const { isLoggingOut } = user; | ||
42 | if (isLoggingOut) { | ||
43 | return ( | ||
44 | <ThemeProvider theme={stores.ui.theme}> | ||
45 | <AppLoader texts={['Logging you out...']} /> | ||
46 | </ThemeProvider> | ||
47 | ); | ||
48 | } | ||
49 | |||
40 | return ( | 50 | return ( |
41 | <ThemeProvider theme={stores.ui.theme}> | 51 | <ThemeProvider theme={stores.ui.theme}> |
42 | <AuthLayout | 52 | <AuthLayout |
@@ -62,6 +72,7 @@ AuthLayoutContainer.wrappedComponent.propTypes = { | |||
62 | stores: PropTypes.shape({ | 72 | stores: PropTypes.shape({ |
63 | app: PropTypes.instanceOf(AppStore).isRequired, | 73 | app: PropTypes.instanceOf(AppStore).isRequired, |
64 | globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, | 74 | globalError: PropTypes.instanceOf(GlobalErrorStore).isRequired, |
75 | user: PropTypes.instanceOf(UserStore).isRequired, | ||
65 | }).isRequired, | 76 | }).isRequired, |
66 | actions: PropTypes.shape({ | 77 | actions: PropTypes.shape({ |
67 | app: PropTypes.shape({ | 78 | app: PropTypes.shape({ |
diff --git a/src/containers/auth/LockedScreen.js b/src/containers/auth/LockedScreen.js new file mode 100644 index 000000000..94285fb06 --- /dev/null +++ b/src/containers/auth/LockedScreen.js | |||
@@ -0,0 +1,72 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { inject, observer } from 'mobx-react'; | ||
4 | import Locked from '../../components/auth/Locked'; | ||
5 | import SettingsStore from '../../stores/SettingsStore'; | ||
6 | import { DEFAULT_LOCK_PASSWORD } from '../../config'; | ||
7 | |||
8 | import { globalError as globalErrorPropType } from '../../prop-types'; | ||
9 | |||
10 | export default @inject('stores', 'actions') @observer class LockedScreen extends Component { | ||
11 | static propTypes = { | ||
12 | error: globalErrorPropType.isRequired, | ||
13 | }; | ||
14 | |||
15 | state = { | ||
16 | error: false, | ||
17 | } | ||
18 | |||
19 | constructor(props) { | ||
20 | super(props); | ||
21 | |||
22 | this.onSubmit = this.onSubmit.bind(this); | ||
23 | } | ||
24 | |||
25 | onSubmit(values) { | ||
26 | const { password } = values; | ||
27 | |||
28 | let correctPassword = this.props.stores.settings.all.app.lockedPassword; | ||
29 | if (!correctPassword) { | ||
30 | // Lock feature was enabled but no password was set | ||
31 | // Use default lock password so user can exit | ||
32 | correctPassword = DEFAULT_LOCK_PASSWORD; | ||
33 | } | ||
34 | |||
35 | if (String(password) === String(correctPassword)) { | ||
36 | this.props.actions.settings.update({ | ||
37 | type: 'app', | ||
38 | data: { | ||
39 | locked: false, | ||
40 | }, | ||
41 | }); | ||
42 | } else { | ||
43 | this.setState({ | ||
44 | error: { | ||
45 | code: 'invalid-credentials', | ||
46 | }, | ||
47 | }); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | render() { | ||
52 | const { stores, error } = this.props; | ||
53 | return ( | ||
54 | <Locked | ||
55 | onSubmit={this.onSubmit} | ||
56 | isSubmitting={stores.user.loginRequest.isExecuting} | ||
57 | error={this.state.error || error} | ||
58 | /> | ||
59 | ); | ||
60 | } | ||
61 | } | ||
62 | |||
63 | LockedScreen.wrappedComponent.propTypes = { | ||
64 | actions: PropTypes.shape({ | ||
65 | settings: PropTypes.shape({ | ||
66 | update: PropTypes.func.isRequired, | ||
67 | }).isRequired, | ||
68 | }).isRequired, | ||
69 | stores: PropTypes.shape({ | ||
70 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | ||
71 | }).isRequired, | ||
72 | }; | ||
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 5563c48bc..a4312d9de 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js | |||
@@ -142,6 +142,7 @@ export default @inject('stores', 'actions') @observer class AppLayoutContainer e | |||
142 | showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar} | 142 | showServicesUpdatedInfoBar={ui.showServicesUpdatedInfoBar} |
143 | appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} | 143 | appUpdateIsDownloaded={app.updateStatus === app.updateStatusTypes.DOWNLOADED} |
144 | nextAppReleaseVersion={app.nextAppReleaseVersion} | 144 | nextAppReleaseVersion={app.nextAppReleaseVersion} |
145 | authRequestFailed={app.authRequestFailed} | ||
145 | sidebar={sidebar} | 146 | sidebar={sidebar} |
146 | workspacesDrawer={workspacesDrawer} | 147 | workspacesDrawer={workspacesDrawer} |
147 | services={servicesContainer} | 148 | services={servicesContainer} |
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js index 88ecd55d5..93ab44690 100644 --- a/src/containers/settings/AccountScreen.js +++ b/src/containers/settings/AccountScreen.js | |||
@@ -28,7 +28,14 @@ export default @inject('stores', 'actions') @observer class AccountScreen extend | |||
28 | handleWebsiteLink(route) { | 28 | handleWebsiteLink(route) { |
29 | const { actions, stores } = this.props; | 29 | const { actions, stores } = this.props; |
30 | 30 | ||
31 | const url = stores.user.getAuthURL(`${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`); | 31 | const api = stores.settings.all.app.server; |
32 | |||
33 | let url; | ||
34 | if (api === 'https://api.franzinfra.com') { | ||
35 | url = stores.user.getAuthURL(`${WEBSITE}${route}?utm_source=app&utm_medium=account_dashboard`); | ||
36 | } else { | ||
37 | url = `${api}${route}`; | ||
38 | } | ||
32 | 39 | ||
33 | actions.app.openExternalUrl({ url }); | 40 | actions.app.openExternalUrl({ url }); |
34 | } | 41 | } |
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js index e4ff03bb3..d18d7fb9b 100644 --- a/src/containers/settings/EditServiceScreen.js +++ b/src/containers/settings/EditServiceScreen.js | |||
@@ -92,6 +92,10 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
92 | intl: intlShape, | 92 | intl: intlShape, |
93 | }; | 93 | }; |
94 | 94 | ||
95 | state = { | ||
96 | isOpeningDarkModeCss: false, | ||
97 | } | ||
98 | |||
95 | onSubmit(data) { | 99 | onSubmit(data) { |
96 | const { action } = this.props.router.params; | 100 | const { action } = this.props.router.params; |
97 | const { recipes, services } = this.props.stores; | 101 | const { recipes, services } = this.props.stores; |
@@ -278,6 +282,28 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
278 | } | 282 | } |
279 | } | 283 | } |
280 | 284 | ||
285 | openDarkmodeCss() { | ||
286 | const { openDarkmodeCss } = this.props.actions.service; | ||
287 | const { action } = this.props.router.params; | ||
288 | |||
289 | if (action === 'edit') { | ||
290 | this.setState({ | ||
291 | isOpeningDarkModeCss: true, | ||
292 | }); | ||
293 | |||
294 | const { activeSettings: service } = this.props.stores.services; | ||
295 | openDarkmodeCss({ | ||
296 | recipe: service.recipe.id, | ||
297 | }); | ||
298 | |||
299 | setTimeout(() => { | ||
300 | this.setState({ | ||
301 | isOpeningDarkModeCss: false, | ||
302 | }); | ||
303 | }, 2500); | ||
304 | } | ||
305 | } | ||
306 | |||
281 | render() { | 307 | render() { |
282 | const { recipes, services, user } = this.props.stores; | 308 | const { recipes, services, user } = this.props.stores; |
283 | const { action } = this.props.router.params; | 309 | const { action } = this.props.router.params; |
@@ -329,6 +355,8 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
329 | isDeleting={services.deleteServiceRequest.isExecuting} | 355 | isDeleting={services.deleteServiceRequest.isExecuting} |
330 | onSubmit={d => this.onSubmit(d)} | 356 | onSubmit={d => this.onSubmit(d)} |
331 | onDelete={() => this.deleteService()} | 357 | onDelete={() => this.deleteService()} |
358 | openDarkmodeCss={() => this.openDarkmodeCss()} | ||
359 | isOpeningDarkModeCss={this.state.isOpeningDarkModeCss} | ||
332 | isProxyFeatureEnabled={proxyFeature.isEnabled} | 360 | isProxyFeatureEnabled={proxyFeature.isEnabled} |
333 | isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan} | 361 | isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan} |
334 | isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan} | 362 | isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan} |
@@ -356,6 +384,7 @@ EditServiceScreen.wrappedComponent.propTypes = { | |||
356 | createService: PropTypes.func.isRequired, | 384 | createService: PropTypes.func.isRequired, |
357 | updateService: PropTypes.func.isRequired, | 385 | updateService: PropTypes.func.isRequired, |
358 | deleteService: PropTypes.func.isRequired, | 386 | deleteService: PropTypes.func.isRequired, |
387 | openDarkmodeCss: PropTypes.func.isRequired, | ||
359 | }).isRequired, | 388 | }).isRequired, |
360 | // settings: PropTypes.shape({ | 389 | // settings: PropTypes.shape({ |
361 | // update: PropTypes.func.isRequred, | 390 | // update: PropTypes.func.isRequred, |
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js index 698b5a3d9..ddee82e45 100644 --- a/src/containers/settings/EditSettingsScreen.js +++ b/src/containers/settings/EditSettingsScreen.js | |||
@@ -1,3 +1,4 @@ | |||
1 | import { ipcRenderer } from 'electron'; | ||
1 | import React, { Component } from 'react'; | 2 | import React, { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 4 | import { inject, observer } from 'mobx-react'; |
@@ -9,7 +10,7 @@ import UserStore from '../../stores/UserStore'; | |||
9 | import TodosStore from '../../features/todos/store'; | 10 | import TodosStore from '../../features/todos/store'; |
10 | import Form from '../../lib/Form'; | 11 | import Form from '../../lib/Form'; |
11 | import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; | 12 | import { APP_LOCALES, SPELLCHECKER_LOCALES } from '../../i18n/languages'; |
12 | import { DEFAULT_APP_SETTINGS } from '../../config'; | 13 | import { DEFAULT_APP_SETTINGS, DEFAULT_LOCK_PASSWORD, HIBERNATION_STRATEGIES } from '../../config'; |
13 | import { config as spellcheckerConfig } from '../../features/spellchecker'; | 14 | import { config as spellcheckerConfig } from '../../features/spellchecker'; |
14 | 15 | ||
15 | import { getSelectOptions } from '../../helpers/i18n-helpers'; | 16 | import { getSelectOptions } from '../../helpers/i18n-helpers'; |
@@ -17,6 +18,8 @@ import { getSelectOptions } from '../../helpers/i18n-helpers'; | |||
17 | import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; | 18 | import EditSettingsForm from '../../components/settings/settings/EditSettingsForm'; |
18 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | 19 | import ErrorBoundary from '../../components/util/ErrorBoundary'; |
19 | 20 | ||
21 | import { API, TODOS_FRONTEND } from '../../environment'; | ||
22 | |||
20 | import globalMessages from '../../i18n/globalMessages'; | 23 | import globalMessages from '../../i18n/globalMessages'; |
21 | import { DEFAULT_IS_FEATURE_ENABLED_BY_USER } from '../../features/todos'; | 24 | import { DEFAULT_IS_FEATURE_ENABLED_BY_USER } from '../../features/todos'; |
22 | import WorkspacesStore from '../../features/workspaces/store'; | 25 | import WorkspacesStore from '../../features/workspaces/store'; |
@@ -25,7 +28,7 @@ import { DEFAULT_SETTING_KEEP_ALL_WORKSPACES_LOADED } from '../../features/works | |||
25 | const messages = defineMessages({ | 28 | const messages = defineMessages({ |
26 | autoLaunchOnStart: { | 29 | autoLaunchOnStart: { |
27 | id: 'settings.app.form.autoLaunchOnStart', | 30 | id: 'settings.app.form.autoLaunchOnStart', |
28 | defaultMessage: '!!!Launch Franz on start', | 31 | defaultMessage: '!!!Launch Ferdi on start', |
29 | }, | 32 | }, |
30 | autoLaunchInBackground: { | 33 | autoLaunchInBackground: { |
31 | id: 'settings.app.form.autoLaunchInBackground', | 34 | id: 'settings.app.form.autoLaunchInBackground', |
@@ -33,15 +36,59 @@ const messages = defineMessages({ | |||
33 | }, | 36 | }, |
34 | runInBackground: { | 37 | runInBackground: { |
35 | id: 'settings.app.form.runInBackground', | 38 | id: 'settings.app.form.runInBackground', |
36 | defaultMessage: '!!!Keep Franz in background when closing the window', | 39 | defaultMessage: '!!!Keep Ferdi in background when closing the window', |
37 | }, | 40 | }, |
38 | enableSystemTray: { | 41 | enableSystemTray: { |
39 | id: 'settings.app.form.enableSystemTray', | 42 | id: 'settings.app.form.enableSystemTray', |
40 | defaultMessage: '!!!Show Franz in system tray', | 43 | defaultMessage: '!!!Show Ferdi in system tray', |
41 | }, | 44 | }, |
42 | minimizeToSystemTray: { | 45 | minimizeToSystemTray: { |
43 | id: 'settings.app.form.minimizeToSystemTray', | 46 | id: 'settings.app.form.minimizeToSystemTray', |
44 | defaultMessage: '!!!Minimize Franz to system tray', | 47 | defaultMessage: '!!!Minimize Ferdi to system tray', |
48 | }, | ||
49 | privateNotifications: { | ||
50 | id: 'settings.app.form.privateNotifications', | ||
51 | defaultMessage: '!!!Don\'t show message content in notifications', | ||
52 | }, | ||
53 | showServiceNavigationBar: { | ||
54 | id: 'settings.app.form.showServiceNavigationBar', | ||
55 | defaultMessage: '!!!Always show service navigation bar', | ||
56 | }, | ||
57 | hibernate: { | ||
58 | id: 'settings.app.form.hibernate', | ||
59 | defaultMessage: '!!!Enable service hibernation', | ||
60 | }, | ||
61 | hibernationStrategy: { | ||
62 | id: 'settings.app.form.hibernationStrategy', | ||
63 | defaultMessage: '!!!Hibernation strategy', | ||
64 | }, | ||
65 | server: { | ||
66 | id: 'settings.app.form.server', | ||
67 | defaultMessage: '!!!Server', | ||
68 | }, | ||
69 | todoServer: { | ||
70 | id: 'settings.app.form.todoServer', | ||
71 | defaultMessage: '!!!Todo Server', | ||
72 | }, | ||
73 | enableLock: { | ||
74 | id: 'settings.app.form.enableLock', | ||
75 | defaultMessage: '!!!Enable Ferdi password lock', | ||
76 | }, | ||
77 | lockPassword: { | ||
78 | id: 'settings.app.form.lockPassword', | ||
79 | defaultMessage: '!!!Ferdi Lock password', | ||
80 | }, | ||
81 | scheduledDNDEnabled: { | ||
82 | id: 'settings.app.form.scheduledDNDEnabled', | ||
83 | defaultMessage: '!!!Enable scheduled Do-not-Disturb', | ||
84 | }, | ||
85 | scheduledDNDStart: { | ||
86 | id: 'settings.app.form.scheduledDNDStart', | ||
87 | defaultMessage: '!!!From', | ||
88 | }, | ||
89 | scheduledDNDEnd: { | ||
90 | id: 'settings.app.form.scheduledDNDEnd', | ||
91 | defaultMessage: '!!!To', | ||
45 | }, | 92 | }, |
46 | language: { | 93 | language: { |
47 | id: 'settings.app.form.language', | 94 | id: 'settings.app.form.language', |
@@ -51,6 +98,14 @@ const messages = defineMessages({ | |||
51 | id: 'settings.app.form.darkMode', | 98 | id: 'settings.app.form.darkMode', |
52 | defaultMessage: '!!!Dark Mode', | 99 | defaultMessage: '!!!Dark Mode', |
53 | }, | 100 | }, |
101 | universalDarkMode: { | ||
102 | id: 'settings.app.form.universalDarkMode', | ||
103 | defaultMessage: '!!!Enable universal Dark Mode', | ||
104 | }, | ||
105 | accentColor: { | ||
106 | id: 'settings.app.form.accentColor', | ||
107 | defaultMessage: '!!!Accent color', | ||
108 | }, | ||
54 | showDisabledServices: { | 109 | showDisabledServices: { |
55 | id: 'settings.app.form.showDisabledServices', | 110 | id: 'settings.app.form.showDisabledServices', |
56 | defaultMessage: '!!!Display disabled services tabs', | 111 | defaultMessage: '!!!Display disabled services tabs', |
@@ -71,6 +126,10 @@ const messages = defineMessages({ | |||
71 | id: 'settings.app.form.beta', | 126 | id: 'settings.app.form.beta', |
72 | defaultMessage: '!!!Include beta versions', | 127 | defaultMessage: '!!!Include beta versions', |
73 | }, | 128 | }, |
129 | noUpdates: { | ||
130 | id: 'settings.app.form.noUpdates', | ||
131 | defaultMessage: '!!!Disable updates', | ||
132 | }, | ||
74 | enableTodos: { | 133 | enableTodos: { |
75 | id: 'settings.app.form.enableTodos', | 134 | id: 'settings.app.form.enableTodos', |
76 | defaultMessage: '!!!Enable Franz Todos', | 135 | defaultMessage: '!!!Enable Franz Todos', |
@@ -107,19 +166,34 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
107 | runInBackground: settingsData.runInBackground, | 166 | runInBackground: settingsData.runInBackground, |
108 | enableSystemTray: settingsData.enableSystemTray, | 167 | enableSystemTray: settingsData.enableSystemTray, |
109 | minimizeToSystemTray: settingsData.minimizeToSystemTray, | 168 | minimizeToSystemTray: settingsData.minimizeToSystemTray, |
169 | privateNotifications: settingsData.privateNotifications, | ||
170 | showServiceNavigationBar: settingsData.showServiceNavigationBar, | ||
171 | hibernate: settingsData.hibernate, | ||
172 | hibernationStrategy: settingsData.hibernationStrategy, | ||
173 | server: settingsData.server, | ||
174 | todoServer: settingsData.todoServer, | ||
175 | lockingFeatureEnabled: settingsData.lockingFeatureEnabled, | ||
176 | lockedPassword: settingsData.lockedPassword, | ||
177 | scheduledDNDEnabled: settingsData.scheduledDNDEnabled, | ||
178 | scheduledDNDStart: settingsData.scheduledDNDStart, | ||
179 | scheduledDNDEnd: settingsData.scheduledDNDEnd, | ||
110 | enableGPUAcceleration: settingsData.enableGPUAcceleration, | 180 | enableGPUAcceleration: settingsData.enableGPUAcceleration, |
111 | showDisabledServices: settingsData.showDisabledServices, | 181 | showDisabledServices: settingsData.showDisabledServices, |
112 | darkMode: settingsData.darkMode, | 182 | darkMode: settingsData.darkMode, |
183 | universalDarkMode: settingsData.universalDarkMode, | ||
184 | accentColor: settingsData.accentColor, | ||
113 | showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted, | 185 | showMessageBadgeWhenMuted: settingsData.showMessageBadgeWhenMuted, |
114 | enableSpellchecking: settingsData.enableSpellchecking, | 186 | enableSpellchecking: settingsData.enableSpellchecking, |
115 | spellcheckerLanguage: settingsData.spellcheckerLanguage, | 187 | spellcheckerLanguage: settingsData.spellcheckerLanguage, |
116 | beta: settingsData.beta, // we need this info in the main process as well | 188 | beta: settingsData.beta, // we need this info in the main process as well |
189 | noUpdates: settingsData.noUpdates, // we need this info in the main process as well | ||
117 | locale: settingsData.locale, // we need this info in the main process as well | 190 | locale: settingsData.locale, // we need this info in the main process as well |
118 | }, | 191 | }, |
119 | }); | 192 | }); |
120 | 193 | ||
121 | user.update({ | 194 | user.update({ |
122 | userData: { | 195 | userData: { |
196 | noUpdates: settingsData.noUpdates, | ||
123 | beta: settingsData.beta, | 197 | beta: settingsData.beta, |
124 | locale: settingsData.locale, | 198 | locale: settingsData.locale, |
125 | }, | 199 | }, |
@@ -140,6 +214,10 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
140 | } | 214 | } |
141 | } | 215 | } |
142 | 216 | ||
217 | openProcessManager() { | ||
218 | ipcRenderer.send('openProcessManager'); | ||
219 | } | ||
220 | |||
143 | prepareForm() { | 221 | prepareForm() { |
144 | const { | 222 | const { |
145 | app, settings, user, todos, workspaces, | 223 | app, settings, user, todos, workspaces, |
@@ -150,6 +228,11 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
150 | locales: APP_LOCALES, | 228 | locales: APP_LOCALES, |
151 | }); | 229 | }); |
152 | 230 | ||
231 | const hibernationStrategies = getSelectOptions({ | ||
232 | locales: HIBERNATION_STRATEGIES, | ||
233 | sort: false, | ||
234 | }); | ||
235 | |||
153 | const spellcheckingLanguages = getSelectOptions({ | 236 | const spellcheckingLanguages = getSelectOptions({ |
154 | locales: SPELLCHECKER_LOCALES, | 237 | locales: SPELLCHECKER_LOCALES, |
155 | automaticDetectionText: this.context.intl.formatMessage(globalMessages.spellcheckerAutomaticDetection), | 238 | automaticDetectionText: this.context.intl.formatMessage(globalMessages.spellcheckerAutomaticDetection), |
@@ -182,6 +265,65 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
182 | value: settings.all.app.minimizeToSystemTray, | 265 | value: settings.all.app.minimizeToSystemTray, |
183 | default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, | 266 | default: DEFAULT_APP_SETTINGS.minimizeToSystemTray, |
184 | }, | 267 | }, |
268 | privateNotifications: { | ||
269 | label: intl.formatMessage(messages.privateNotifications), | ||
270 | value: settings.all.app.privateNotifications, | ||
271 | default: DEFAULT_APP_SETTINGS.privateNotifications, | ||
272 | }, | ||
273 | showServiceNavigationBar: { | ||
274 | label: intl.formatMessage(messages.showServiceNavigationBar), | ||
275 | value: settings.all.app.showServiceNavigationBar, | ||
276 | default: DEFAULT_APP_SETTINGS.showServiceNavigationBar, | ||
277 | }, | ||
278 | hibernate: { | ||
279 | label: intl.formatMessage(messages.hibernate), | ||
280 | value: settings.all.app.hibernate, | ||
281 | default: DEFAULT_APP_SETTINGS.hibernate, | ||
282 | }, | ||
283 | hibernationStrategy: { | ||
284 | label: intl.formatMessage(messages.hibernationStrategy), | ||
285 | value: settings.all.app.hibernationStrategy, | ||
286 | options: hibernationStrategies, | ||
287 | default: DEFAULT_APP_SETTINGS.hibernationStrategy, | ||
288 | }, | ||
289 | server: { | ||
290 | label: intl.formatMessage(messages.server), | ||
291 | value: settings.all.app.server || API, | ||
292 | default: API, | ||
293 | }, | ||
294 | todoServer: { | ||
295 | label: intl.formatMessage(messages.todoServer), | ||
296 | value: settings.all.app.todoServer, | ||
297 | default: TODOS_FRONTEND, | ||
298 | }, | ||
299 | lockingFeatureEnabled: { | ||
300 | label: intl.formatMessage(messages.enableLock), | ||
301 | value: settings.all.app.lockingFeatureEnabled || false, | ||
302 | default: false, | ||
303 | }, | ||
304 | lockedPassword: { | ||
305 | label: intl.formatMessage(messages.lockPassword), | ||
306 | value: settings.all.app.lockedPassword, | ||
307 | default: DEFAULT_LOCK_PASSWORD, | ||
308 | type: 'password', | ||
309 | }, | ||
310 | scheduledDNDEnabled: { | ||
311 | label: intl.formatMessage(messages.scheduledDNDEnabled), | ||
312 | value: settings.all.app.scheduledDNDEnabled || false, | ||
313 | default: false, | ||
314 | }, | ||
315 | scheduledDNDStart: { | ||
316 | label: intl.formatMessage(messages.scheduledDNDStart), | ||
317 | value: settings.all.app.scheduledDNDStart, | ||
318 | default: '17:00', | ||
319 | type: 'time', | ||
320 | }, | ||
321 | scheduledDNDEnd: { | ||
322 | label: intl.formatMessage(messages.scheduledDNDEnd), | ||
323 | value: settings.all.app.scheduledDNDEnd, | ||
324 | default: '09:00', | ||
325 | type: 'time', | ||
326 | }, | ||
185 | showDisabledServices: { | 327 | showDisabledServices: { |
186 | label: intl.formatMessage(messages.showDisabledServices), | 328 | label: intl.formatMessage(messages.showDisabledServices), |
187 | value: settings.all.app.showDisabledServices, | 329 | value: settings.all.app.showDisabledServices, |
@@ -208,6 +350,16 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
208 | value: settings.all.app.darkMode, | 350 | value: settings.all.app.darkMode, |
209 | default: DEFAULT_APP_SETTINGS.darkMode, | 351 | default: DEFAULT_APP_SETTINGS.darkMode, |
210 | }, | 352 | }, |
353 | universalDarkMode: { | ||
354 | label: intl.formatMessage(messages.universalDarkMode), | ||
355 | value: settings.all.app.universalDarkMode, | ||
356 | default: DEFAULT_APP_SETTINGS.universalDarkMode, | ||
357 | }, | ||
358 | accentColor: { | ||
359 | label: intl.formatMessage(messages.accentColor), | ||
360 | value: settings.all.app.accentColor, | ||
361 | default: DEFAULT_APP_SETTINGS.accentColor, | ||
362 | }, | ||
211 | enableGPUAcceleration: { | 363 | enableGPUAcceleration: { |
212 | label: intl.formatMessage(messages.enableGPUAcceleration), | 364 | label: intl.formatMessage(messages.enableGPUAcceleration), |
213 | value: settings.all.app.enableGPUAcceleration, | 365 | value: settings.all.app.enableGPUAcceleration, |
@@ -224,6 +376,11 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
224 | value: user.data.beta, | 376 | value: user.data.beta, |
225 | default: DEFAULT_APP_SETTINGS.beta, | 377 | default: DEFAULT_APP_SETTINGS.beta, |
226 | }, | 378 | }, |
379 | noUpdates: { | ||
380 | label: intl.formatMessage(messages.noUpdates), | ||
381 | value: settings.app.noUpdates, | ||
382 | default: DEFAULT_APP_SETTINGS.noUpdates, | ||
383 | }, | ||
227 | }, | 384 | }, |
228 | }; | 385 | }; |
229 | 386 | ||
@@ -257,6 +414,7 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
257 | cacheSize, | 414 | cacheSize, |
258 | updateStatusTypes, | 415 | updateStatusTypes, |
259 | isClearingAllCache, | 416 | isClearingAllCache, |
417 | lockingFeatureEnabled, | ||
260 | } = app; | 418 | } = app; |
261 | const { | 419 | const { |
262 | checkForUpdates, | 420 | checkForUpdates, |
@@ -282,6 +440,12 @@ export default @inject('stores', 'actions') @observer class EditSettingsScreen e | |||
282 | isSpellcheckerIncludedInCurrentPlan={spellcheckerConfig.isIncludedInCurrentPlan} | 440 | isSpellcheckerIncludedInCurrentPlan={spellcheckerConfig.isIncludedInCurrentPlan} |
283 | isTodosEnabled={todos.isFeatureActive} | 441 | isTodosEnabled={todos.isFeatureActive} |
284 | isWorkspaceEnabled={workspaces.isFeatureActive} | 442 | isWorkspaceEnabled={workspaces.isFeatureActive} |
443 | server={this.props.stores.settings.app.server} | ||
444 | lockingFeatureEnabled={lockingFeatureEnabled} | ||
445 | noUpdates={this.props.stores.settings.app.noUpdates} | ||
446 | hibernationEnabled={this.props.stores.settings.app.hibernate} | ||
447 | isDarkmodeEnabled={this.props.stores.settings.app.darkMode} | ||
448 | openProcessManager={() => this.openProcessManager()} | ||
285 | /> | 449 | /> |
286 | </ErrorBoundary> | 450 | </ErrorBoundary> |
287 | ); | 451 | ); |
diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js index 132820b6f..70b599d9c 100644 --- a/src/containers/settings/RecipesScreen.js +++ b/src/containers/settings/RecipesScreen.js | |||
@@ -1,4 +1,5 @@ | |||
1 | import { remote, shell } from 'electron'; | 1 | import { remote, shell } from 'electron'; |
2 | import fs from 'fs-extra'; | ||
2 | import React, { Component } from 'react'; | 3 | import React, { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 4 | import PropTypes from 'prop-types'; |
4 | import { autorun } from 'mobx'; | 5 | import { autorun } from 'mobx'; |
@@ -12,9 +13,9 @@ import UserStore from '../../stores/UserStore'; | |||
12 | 13 | ||
13 | import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; | 14 | import RecipesDashboard from '../../components/settings/recipes/RecipesDashboard'; |
14 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | 15 | import ErrorBoundary from '../../components/util/ErrorBoundary'; |
15 | import { FRANZ_DEV_DOCS } from '../../config'; | 16 | import { FRANZ_DEV_DOCS, RECIPES_PATH } from '../../config'; |
16 | import { gaEvent } from '../../lib/analytics'; | ||
17 | import { communityRecipesStore } from '../../features/communityRecipes'; | 17 | import { communityRecipesStore } from '../../features/communityRecipes'; |
18 | import RecipePreview from '../../models/RecipePreview'; | ||
18 | 19 | ||
19 | const { app } = remote; | 20 | const { app } = remote; |
20 | 21 | ||
@@ -38,6 +39,14 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend | |||
38 | 39 | ||
39 | autorunDisposer = null; | 40 | autorunDisposer = null; |
40 | 41 | ||
42 | customRecipes = []; | ||
43 | |||
44 | constructor(props) { | ||
45 | super(props); | ||
46 | |||
47 | this.customRecipes = fs.readJsonSync(path.join(RECIPES_PATH, 'all.json')); | ||
48 | } | ||
49 | |||
41 | componentDidMount() { | 50 | componentDidMount() { |
42 | this.autorunDisposer = autorun(() => { | 51 | this.autorunDisposer = autorun(() => { |
43 | const { filter } = this.props.params; | 52 | const { filter } = this.props.params; |
@@ -68,6 +77,27 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend | |||
68 | } | 77 | } |
69 | } | 78 | } |
70 | 79 | ||
80 | |||
81 | prepareRecipes(recipes) { | ||
82 | return recipes | ||
83 | // Filter out duplicate recipes | ||
84 | .filter((recipe, index, self) => { | ||
85 | const ids = self.map(rec => rec.id); | ||
86 | return ids.indexOf(recipe.id) === index; | ||
87 | |||
88 | // Sort alphabetically | ||
89 | }).sort((a, b) => { | ||
90 | if (a.id < b.id) { return -1; } | ||
91 | if (a.id > b.id) { return 1; } | ||
92 | return 0; | ||
93 | }); | ||
94 | } | ||
95 | |||
96 | // Create an array of RecipePreviews from an array of recipe objects | ||
97 | createPreviews(recipes) { | ||
98 | return recipes.map(recipe => new RecipePreview(recipe)); | ||
99 | } | ||
100 | |||
71 | resetSearch() { | 101 | resetSearch() { |
72 | this.setState({ needle: null }); | 102 | this.setState({ needle: null }); |
73 | } | 103 | } |
@@ -89,14 +119,25 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend | |||
89 | let recipeFilter; | 119 | let recipeFilter; |
90 | 120 | ||
91 | if (filter === 'all') { | 121 | if (filter === 'all') { |
92 | recipeFilter = recipePreviews.all; | 122 | recipeFilter = this.prepareRecipes([ |
123 | ...recipePreviews.all, | ||
124 | ...this.createPreviews(this.customRecipes), | ||
125 | ]); | ||
93 | } else if (filter === 'dev') { | 126 | } else if (filter === 'dev') { |
94 | recipeFilter = communityRecipesStore.communityRecipes; | 127 | recipeFilter = communityRecipesStore.communityRecipes; |
95 | } else { | 128 | } else { |
96 | recipeFilter = recipePreviews.featured; | 129 | recipeFilter = recipePreviews.featured; |
97 | } | 130 | } |
98 | 131 | ||
99 | const allRecipes = this.state.needle ? recipePreviews.searchResults : recipeFilter; | 132 | const allRecipes = this.state.needle ? this.prepareRecipes([ |
133 | // All search recipes from server | ||
134 | ...recipePreviews.searchResults, | ||
135 | // All search recipes from local recipes | ||
136 | ...this.createPreviews( | ||
137 | this.customRecipes | ||
138 | .filter(service => service.name.toLowerCase().includes(this.state.needle.toLowerCase())), | ||
139 | ), | ||
140 | ]) : recipeFilter; | ||
100 | 141 | ||
101 | const isLoading = recipePreviews.featuredRecipePreviewsRequest.isExecuting | 142 | const isLoading = recipePreviews.featuredRecipePreviewsRequest.isExecuting |
102 | || recipePreviews.allRecipePreviewsRequest.isExecuting | 143 | || recipePreviews.allRecipePreviewsRequest.isExecuting |
@@ -122,11 +163,9 @@ export default @inject('stores', 'actions') @observer class RecipesScreen extend | |||
122 | recipeDirectory={recipeDirectory} | 163 | recipeDirectory={recipeDirectory} |
123 | openRecipeDirectory={() => { | 164 | openRecipeDirectory={() => { |
124 | shell.openItem(recipeDirectory); | 165 | shell.openItem(recipeDirectory); |
125 | gaEvent('Recipe', 'open-recipe-folder', 'Open Folder'); | ||
126 | }} | 166 | }} |
127 | openDevDocs={() => { | 167 | openDevDocs={() => { |
128 | appActions.openExternalUrl({ url: FRANZ_DEV_DOCS }); | 168 | appActions.openExternalUrl({ url: FRANZ_DEV_DOCS }); |
129 | gaEvent('Recipe', 'open-dev-docs', 'Developer Documentation'); | ||
130 | }} | 169 | }} |
131 | isCommunityRecipesIncludedInCurrentPlan={communityRecipesStore.isCommunityRecipesIncludedInCurrentPlan} | 170 | isCommunityRecipesIncludedInCurrentPlan={communityRecipesStore.isCommunityRecipesIncludedInCurrentPlan} |
132 | isUserPremiumUser={user.isPremium} | 171 | isUserPremiumUser={user.isPremium} |
diff --git a/src/containers/settings/SupportScreen.js b/src/containers/settings/SupportScreen.js new file mode 100644 index 000000000..34dce1dae --- /dev/null +++ b/src/containers/settings/SupportScreen.js | |||
@@ -0,0 +1,36 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import { inject } from 'mobx-react'; | ||
3 | import PropTypes from 'prop-types'; | ||
4 | |||
5 | import SupportFerdi from '../../components/settings/supportFerdi/SupportFerdiDashboard'; | ||
6 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | ||
7 | |||
8 | export default @inject('actions') class SupportScreen extends Component { | ||
9 | constructor(props) { | ||
10 | super(props); | ||
11 | |||
12 | this.openLink = this.openLink.bind(this); | ||
13 | } | ||
14 | |||
15 | openLink(url) { | ||
16 | this.props.actions.app.openExternalUrl({ url }); | ||
17 | } | ||
18 | |||
19 | render() { | ||
20 | return ( | ||
21 | <ErrorBoundary> | ||
22 | <SupportFerdi | ||
23 | openLink={this.openLink} | ||
24 | /> | ||
25 | </ErrorBoundary> | ||
26 | ); | ||
27 | } | ||
28 | } | ||
29 | |||
30 | SupportScreen.wrappedComponent.propTypes = { | ||
31 | actions: PropTypes.shape({ | ||
32 | app: PropTypes.shape({ | ||
33 | openExternalUrl: PropTypes.func.isRequired, | ||
34 | }).isRequired, | ||
35 | }).isRequired, | ||
36 | }; | ||
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js index f600c9947..d0196923a 100644 --- a/src/containers/settings/TeamScreen.js +++ b/src/containers/settings/TeamScreen.js | |||
@@ -4,6 +4,7 @@ import { inject, observer } from 'mobx-react'; | |||
4 | 4 | ||
5 | import UserStore from '../../stores/UserStore'; | 5 | import UserStore from '../../stores/UserStore'; |
6 | import AppStore from '../../stores/AppStore'; | 6 | import AppStore from '../../stores/AppStore'; |
7 | import SettingsStore from '../../stores/SettingsStore'; | ||
7 | 8 | ||
8 | import TeamDashboard from '../../components/settings/team/TeamDashboard'; | 9 | import TeamDashboard from '../../components/settings/team/TeamDashboard'; |
9 | import ErrorBoundary from '../../components/util/ErrorBoundary'; | 10 | import ErrorBoundary from '../../components/util/ErrorBoundary'; |
@@ -19,9 +20,10 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C | |||
19 | } | 20 | } |
20 | 21 | ||
21 | render() { | 22 | render() { |
22 | const { user } = this.props.stores; | 23 | const { user, settings } = this.props.stores; |
23 | 24 | ||
24 | const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; | 25 | const isLoadingUserInfo = user.getUserInfoRequest.isExecuting; |
26 | const { server } = settings.app; | ||
25 | 27 | ||
26 | return ( | 28 | return ( |
27 | <ErrorBoundary> | 29 | <ErrorBoundary> |
@@ -31,6 +33,7 @@ export default @inject('stores', 'actions') @observer class TeamScreen extends C | |||
31 | retryUserInfoRequest={() => this.reloadData()} | 33 | retryUserInfoRequest={() => this.reloadData()} |
32 | openTeamManagement={() => this.handleWebsiteLink('/user/team')} | 34 | openTeamManagement={() => this.handleWebsiteLink('/user/team')} |
33 | isProUser={user.isPro} | 35 | isProUser={user.isPro} |
36 | server={server} | ||
34 | /> | 37 | /> |
35 | </ErrorBoundary> | 38 | </ErrorBoundary> |
36 | ); | 39 | ); |
@@ -41,6 +44,7 @@ TeamScreen.wrappedComponent.propTypes = { | |||
41 | stores: PropTypes.shape({ | 44 | stores: PropTypes.shape({ |
42 | user: PropTypes.instanceOf(UserStore).isRequired, | 45 | user: PropTypes.instanceOf(UserStore).isRequired, |
43 | app: PropTypes.instanceOf(AppStore).isRequired, | 46 | app: PropTypes.instanceOf(AppStore).isRequired, |
47 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | ||
44 | }).isRequired, | 48 | }).isRequired, |
45 | actions: PropTypes.shape({ | 49 | actions: PropTypes.shape({ |
46 | payment: PropTypes.shape({ | 50 | payment: PropTypes.shape({ |