diff options
author | Bennett <hello@vantezzen.io> | 2020-04-17 14:32:53 +0200 |
---|---|---|
committer | GitHub <noreply@github.com> | 2020-04-17 13:32:53 +0100 |
commit | 3239172af9fb68226e1e11392b16f833b726ad72 (patch) | |
tree | fdafdea17c8873cffdec64351e532ffdb2c7d233 /src | |
parent | New Crowdin translations (#570) (diff) | |
download | ferdium-app-3239172af9fb68226e1e11392b16f833b726ad72.tar.gz ferdium-app-3239172af9fb68226e1e11392b16f833b726ad72.tar.zst ferdium-app-3239172af9fb68226e1e11392b16f833b726ad72.zip |
Merge Franz 5.5.0 beta.1 and 2 (#549)
* Automatic i18n update (i18n.meetfranz.com)
* Fix zoom not working
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Add environment variable
FRANZ_APPDATA_DIR
* Add environment variable for config
FRANZ_APPDATA_DIR
* fix stuff that shouldn't need fixing in the first place
* notarize app
* bump version to 5.4.1
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* update to electron 7.1.7
* bump version to 5.5.0
* Automatic i18n update (i18n.meetfranz.com)
* Bump electron to 7.1.10
* plan changes
* filter plan variants
* add missing string
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Replace/remove deprecated electron function calls
Electron replaced several function calls with object properties.
Franz still uses some of these deprecated functions, so this commit will replace these with their new property counterpart.
This commit is only transferring the changes made in getferdi/ferdi#371 and getferdi/ferdi#384 into the upstream repository.
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Update to electron 8.0.1
* feat(Service): Add service hibernation to save system resources
* Fix linting issues
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Bump version to electron 8.1.1
* update strings
* update electron-notarize
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Automatic i18n update (i18n.meetfranz.com)
* Add audio & video permission request for cataline
* Automatic i18n update (i18n.meetfranz.com)
* bump version to beta 1
* Automatic i18n update (i18n.meetfranz.com)
* enable external links
* Fix main content href
* Automatic i18n update (i18n.meetfranz.com)
* fix(Service): Fix issue with user agent override in service workers
* Automatic i18n update (i18n.meetfranz.com)
* fix macOS string replacement
* Automatic i18n update (i18n.meetfranz.com)
* fix(Experimental): Fix Google signin
* fix(Service): Add integrity checks to prevent services recipes getting lost
* Remove service reload on app-resume
* update strings
* bump version to beta 2
* Automatic i18n update (i18n.meetfranz.com)
* bump electron-builder and electron-updater deps
* test video permissions
* fix `extendInfo` keys
* fix typo
* fix permission request
* update camera/microphone entitlements
* Automatic i18n update (i18n.meetfranz.com)
* Test screen capture permissions
* unpack `mac-screen-capture-permissions`
* don't require localstorage temp key for permissions request
* move macOS permission request to main process
* Fix checking for permissions
* fix(macOS): Ask the user to move Franz to the `/Applications` Folder
* only try to recover recipe when user is online
* update service request link
* Update changelog
* Reset `service.lostRecipeReloadAttempt` once service is alive again
* Update translations
* Fix crash on macOS
* Fix merge errors
* Fix lint
* Disable ConnectionLostBanner
* #551 Add information about Ferdi to the user agent
* Fix requested changes
* Remove tsbuildinfo files
* Add .tsbuildinfo files to gitignore
* Fix "Cannot destructure property 'app' of '_electron.remote' as it is undefined"
Co-authored-by: FranzBot <i18n@meetfranz.com>
Co-authored-by: Makazzz <makazzzpro@live.ca>
Co-authored-by: Stefan Malzner <stefan@adlk.io>
Diffstat (limited to 'src')
31 files changed, 1254 insertions, 168 deletions
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js new file mode 100644 index 000000000..9609a65b1 --- /dev/null +++ b/src/components/services/content/ConnectionLostBanner.js | |||
@@ -0,0 +1,119 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | import { intlShape, defineMessages } from 'react-intl'; | ||
7 | |||
8 | import { | ||
9 | mdiAlert, | ||
10 | } from '@mdi/js'; | ||
11 | import { LIVE_API_WEBSITE } from '../../../config'; | ||
12 | // import { Button } from '@meetfranz/forms'; | ||
13 | |||
14 | const messages = defineMessages({ | ||
15 | text: { | ||
16 | id: 'connectionLostBanner.message', | ||
17 | defaultMessage: '!!!Oh no! Franz lost the connection to {name}.', | ||
18 | }, | ||
19 | moreInformation: { | ||
20 | id: 'connectionLostBanner.informationLink', | ||
21 | defaultMessage: '!!!What happened?', | ||
22 | }, | ||
23 | cta: { | ||
24 | id: 'connectionLostBanner.cta', | ||
25 | defaultMessage: '!!!Reload Service', | ||
26 | }, | ||
27 | }); | ||
28 | |||
29 | const styles = theme => ({ | ||
30 | root: { | ||
31 | background: theme.colorBackground, | ||
32 | borderRadius: theme.borderRadius, | ||
33 | position: 'absolute', | ||
34 | zIndex: 300, | ||
35 | height: 50, | ||
36 | display: 'flex', | ||
37 | flexDirection: 'row', | ||
38 | alignItems: 'center', | ||
39 | bottom: 10, | ||
40 | right: 10, | ||
41 | justifyContent: 'center', | ||
42 | padding: 10, | ||
43 | fontSize: 12, | ||
44 | }, | ||
45 | link: { | ||
46 | display: 'inline-flex', | ||
47 | opacity: 0.7, | ||
48 | }, | ||
49 | button: { | ||
50 | transition: 'opacity 0.25s', | ||
51 | color: theme.colorText, | ||
52 | border: [1, 'solid', theme.colorText], | ||
53 | borderRadius: theme.borderRadiusSmall, | ||
54 | padding: 4, | ||
55 | fontSize: 12, | ||
56 | marginLeft: 15, | ||
57 | |||
58 | '&:hover': { | ||
59 | opacity: 0.8, | ||
60 | }, | ||
61 | }, | ||
62 | icon: { | ||
63 | marginRight: 10, | ||
64 | fill: theme.styleTypes.danger.accent, | ||
65 | }, | ||
66 | }); | ||
67 | |||
68 | @injectSheet(styles) @observer | ||
69 | class ConnectionLostBanner extends Component { | ||
70 | static propTypes = { | ||
71 | classes: PropTypes.object.isRequired, | ||
72 | name: PropTypes.string.isRequired, | ||
73 | reload: PropTypes.func.isRequired, | ||
74 | } | ||
75 | |||
76 | static contextTypes = { | ||
77 | intl: intlShape, | ||
78 | }; | ||
79 | |||
80 | inputRef = React.createRef(); | ||
81 | |||
82 | render() { | ||
83 | const { | ||
84 | classes, | ||
85 | name, | ||
86 | reload, | ||
87 | } = this.props; | ||
88 | |||
89 | const { intl } = this.context; | ||
90 | |||
91 | return ( | ||
92 | <div className={classes.root}> | ||
93 | <Icon | ||
94 | icon={mdiAlert} | ||
95 | className={classes.icon} | ||
96 | /> | ||
97 | <p> | ||
98 | {intl.formatMessage(messages.text, { name })} | ||
99 | <br /> | ||
100 | <a | ||
101 | href={`${LIVE_API_WEBSITE}/support#what-does-franz-lost-the-connection-to-service-mean`} | ||
102 | className={classes.link} | ||
103 | > | ||
104 | {intl.formatMessage(messages.moreInformation)} | ||
105 | </a> | ||
106 | </p> | ||
107 | <button | ||
108 | type="button" | ||
109 | className={classes.button} | ||
110 | onClick={reload} | ||
111 | > | ||
112 | {intl.formatMessage(messages.cta)} | ||
113 | </button> | ||
114 | </div> | ||
115 | ); | ||
116 | } | ||
117 | } | ||
118 | |||
119 | export default ConnectionLostBanner; | ||
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js index f6832038a..d91016c71 100644 --- a/src/components/services/content/ServiceView.js +++ b/src/components/services/content/ServiceView.js | |||
@@ -193,7 +193,7 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
193 | </Fragment> | 193 | </Fragment> |
194 | ) : ( | 194 | ) : ( |
195 | <> | 195 | <> |
196 | {!service.isHibernating ? ( | 196 | {(!service.isHibernating || service.disableHibernation) ? ( |
197 | <> | 197 | <> |
198 | {showNavBar && ( | 198 | {showNavBar && ( |
199 | <WebControlsScreen service={service} /> | 199 | <WebControlsScreen service={service} /> |
@@ -203,6 +203,12 @@ export default @inject('stores', 'actions') @observer class ServiceView extends | |||
203 | setWebviewReference={setWebviewReference} | 203 | setWebviewReference={setWebviewReference} |
204 | detachService={detachService} | 204 | detachService={detachService} |
205 | /> | 205 | /> |
206 | {/* {service.lostRecipeConnection && ( | ||
207 | <ConnectionLostBanner | ||
208 | name={service.name} | ||
209 | reload={reload} | ||
210 | /> | ||
211 | )} */} | ||
206 | </> | 212 | </> |
207 | ) : ( | 213 | ) : ( |
208 | <div> | 214 | <div> |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 36338a910..ea7a66a62 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -145,7 +145,7 @@ class TabItem extends Component { | |||
145 | • | 145 | • |
146 | </span> | 146 | </span> |
147 | )} | 147 | )} |
148 | {service.isHibernating && ( | 148 | {service.isHibernating && !service.disableHibernation && ( |
149 | <span className="tab-item__message-count hibernating"> | 149 | <span className="tab-item__message-count hibernating"> |
150 | • | 150 | • |
151 | </span> | 151 | </span> |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index bb4f4a76f..4fd1f99ef 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -94,6 +94,10 @@ const messages = defineMessages({ | |||
94 | id: 'settings.service.form.isMutedInfo', | 94 | id: 'settings.service.form.isMutedInfo', |
95 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', | 95 | defaultMessage: '!!!When disabled, all notification sounds and audio playback are muted', |
96 | }, | 96 | }, |
97 | disableHibernationInfo: { | ||
98 | id: 'settings.service.form.disableHibernationInfo', | ||
99 | defaultMessage: '!!!You currently have hibernation enabled but you can disable hibernation for individual services using this option.', | ||
100 | }, | ||
97 | headlineNotifications: { | 101 | headlineNotifications: { |
98 | id: 'settings.service.form.headlineNotifications', | 102 | id: 'settings.service.form.headlineNotifications', |
99 | defaultMessage: '!!!Notifications', | 103 | defaultMessage: '!!!Notifications', |
@@ -154,6 +158,7 @@ export default @observer class EditServiceForm extends Component { | |||
154 | isProxyFeatureEnabled: PropTypes.bool.isRequired, | 158 | isProxyFeatureEnabled: PropTypes.bool.isRequired, |
155 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, | 159 | isServiceProxyIncludedInCurrentPlan: PropTypes.bool.isRequired, |
156 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, | 160 | isSpellcheckerIncludedInCurrentPlan: PropTypes.bool.isRequired, |
161 | isHibernationFeatureActive: PropTypes.bool.isRequired, | ||
157 | }; | 162 | }; |
158 | 163 | ||
159 | static defaultProps = { | 164 | static defaultProps = { |
@@ -219,6 +224,7 @@ export default @observer class EditServiceForm extends Component { | |||
219 | isProxyFeatureEnabled, | 224 | isProxyFeatureEnabled, |
220 | isServiceProxyIncludedInCurrentPlan, | 225 | isServiceProxyIncludedInCurrentPlan, |
221 | isSpellcheckerIncludedInCurrentPlan, | 226 | isSpellcheckerIncludedInCurrentPlan, |
227 | isHibernationFeatureActive, | ||
222 | } = this.props; | 228 | } = this.props; |
223 | const { intl } = this.context; | 229 | const { intl } = this.context; |
224 | 230 | ||
@@ -365,6 +371,14 @@ export default @observer class EditServiceForm extends Component { | |||
365 | <div className="settings__settings-group"> | 371 | <div className="settings__settings-group"> |
366 | <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> | 372 | <h3>{intl.formatMessage(messages.headlineGeneral)}</h3> |
367 | <Toggle field={form.$('isEnabled')} /> | 373 | <Toggle field={form.$('isEnabled')} /> |
374 | {isHibernationFeatureActive && ( | ||
375 | <> | ||
376 | <Toggle field={form.$('disableHibernation')} /> | ||
377 | <p className="settings__help"> | ||
378 | {intl.formatMessage(messages.disableHibernationInfo)} | ||
379 | </p> | ||
380 | </> | ||
381 | )} | ||
368 | <Toggle field={form.$('isDarkModeEnabled')} /> | 382 | <Toggle field={form.$('isDarkModeEnabled')} /> |
369 | {form.$('isDarkModeEnabled').value | 383 | {form.$('isDarkModeEnabled').value |
370 | && ( | 384 | && ( |
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js index f1039709c..dbc2a9078 100644 --- a/src/components/ui/FeatureList.js +++ b/src/components/ui/FeatureList.js | |||
@@ -66,6 +66,10 @@ const messages = defineMessages({ | |||
66 | id: 'pricing.features.adFree', | 66 | id: 'pricing.features.adFree', |
67 | defaultMessage: '!!!Forever ad-free', | 67 | defaultMessage: '!!!Forever ad-free', |
68 | }, | 68 | }, |
69 | appDelayEnabled: { | ||
70 | id: 'pricing.features.appDelaysEnabled', | ||
71 | defaultMessage: '!!!Occasional Waiting Screens', | ||
72 | }, | ||
69 | }); | 73 | }); |
70 | 74 | ||
71 | export class FeatureList extends Component { | 75 | export class FeatureList extends Component { |
@@ -96,6 +100,7 @@ export class FeatureList extends Component { | |||
96 | const features = []; | 100 | const features = []; |
97 | if (plan === PLANS.FREE) { | 101 | if (plan === PLANS.FREE) { |
98 | features.push( | 102 | features.push( |
103 | messages.appDelayEnabled, | ||
99 | messages.upToThreeServices, | 104 | messages.upToThreeServices, |
100 | messages.availableRecipes, | 105 | messages.availableRecipes, |
101 | messages.accountSync, | 106 | messages.accountSync, |
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js index 560068efc..14c1ef41e 100644 --- a/src/containers/settings/EditServiceScreen.js +++ b/src/containers/settings/EditServiceScreen.js | |||
@@ -33,6 +33,10 @@ const messages = defineMessages({ | |||
33 | id: 'settings.service.form.enableService', | 33 | id: 'settings.service.form.enableService', |
34 | defaultMessage: '!!!Enable service', | 34 | defaultMessage: '!!!Enable service', |
35 | }, | 35 | }, |
36 | disableHibernation: { | ||
37 | id: 'settings.service.form.disableHibernation', | ||
38 | defaultMessage: '!!!Disable hibernation', | ||
39 | }, | ||
36 | enableNotification: { | 40 | enableNotification: { |
37 | id: 'settings.service.form.enableNotification', | 41 | id: 'settings.service.form.enableNotification', |
38 | defaultMessage: '!!!Enable Notifications', | 42 | defaultMessage: '!!!Enable Notifications', |
@@ -134,8 +138,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
134 | 138 | ||
135 | const { | 139 | const { |
136 | stores, | 140 | stores, |
141 | router, | ||
137 | } = this.props; | 142 | } = this.props; |
138 | 143 | ||
144 | const { action } = router.params; | ||
145 | |||
139 | let defaultSpellcheckerLanguage = SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage]; | 146 | let defaultSpellcheckerLanguage = SPELLCHECKER_LOCALES[stores.settings.app.spellcheckerLanguage]; |
140 | 147 | ||
141 | if (stores.settings.app.spellcheckerLanguage === 'automatic') { | 148 | if (stores.settings.app.spellcheckerLanguage === 'automatic') { |
@@ -160,6 +167,11 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
160 | value: service.isEnabled, | 167 | value: service.isEnabled, |
161 | default: true, | 168 | default: true, |
162 | }, | 169 | }, |
170 | disableHibernation: { | ||
171 | label: intl.formatMessage(messages.disableHibernation), | ||
172 | value: action !== 'edit' ? false : service.disableHibernation, | ||
173 | default: false, | ||
174 | }, | ||
163 | isNotificationEnabled: { | 175 | isNotificationEnabled: { |
164 | label: intl.formatMessage(messages.enableNotification), | 176 | label: intl.formatMessage(messages.enableNotification), |
165 | value: service.isNotificationEnabled, | 177 | value: service.isNotificationEnabled, |
@@ -327,7 +339,9 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
327 | } | 339 | } |
328 | 340 | ||
329 | render() { | 341 | render() { |
330 | const { recipes, services, user } = this.props.stores; | 342 | const { |
343 | recipes, services, user, settings, | ||
344 | } = this.props.stores; | ||
331 | const { action } = this.props.router.params; | 345 | const { action } = this.props.router.params; |
332 | 346 | ||
333 | let recipe; | 347 | let recipe; |
@@ -381,6 +395,7 @@ export default @inject('stores', 'actions') @observer class EditServiceScreen ex | |||
381 | isProxyFeatureEnabled={proxyFeature.isEnabled} | 395 | isProxyFeatureEnabled={proxyFeature.isEnabled} |
382 | isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan} | 396 | isServiceProxyIncludedInCurrentPlan={proxyFeature.isIncludedInCurrentPlan} |
383 | isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan} | 397 | isSpellcheckerIncludedInCurrentPlan={spellcheckerFeature.isIncludedInCurrentPlan} |
398 | isHibernationFeatureActive={settings.app.hibernate} | ||
384 | /> | 399 | /> |
385 | </ErrorBoundary> | 400 | </ErrorBoundary> |
386 | ); | 401 | ); |
diff --git a/src/electron/macOSPermissions.js b/src/electron/macOSPermissions.js new file mode 100644 index 000000000..4ba6a7619 --- /dev/null +++ b/src/electron/macOSPermissions.js | |||
@@ -0,0 +1,14 @@ | |||
1 | import { systemPreferences } from 'electron'; | ||
2 | import { | ||
3 | hasScreenCapturePermission, | ||
4 | hasPromptedForPermission, | ||
5 | } from 'mac-screen-capture-permissions'; | ||
6 | |||
7 | export default function () { | ||
8 | systemPreferences.askForMediaAccess('camera'); | ||
9 | systemPreferences.askForMediaAccess('microphone'); | ||
10 | |||
11 | if (!hasPromptedForPermission()) { | ||
12 | hasScreenCapturePermission(); | ||
13 | } | ||
14 | } | ||
diff --git a/src/features/announcements/components/AnnouncementScreen.js b/src/features/announcements/components/AnnouncementScreen.js index 38de2dbc8..2f25e7139 100644 --- a/src/features/announcements/components/AnnouncementScreen.js +++ b/src/features/announcements/components/AnnouncementScreen.js | |||
@@ -192,6 +192,11 @@ class AnnouncementScreen extends Component { | |||
192 | stores: PropTypes.shape({ | 192 | stores: PropTypes.shape({ |
193 | ui: PropTypes.instanceOf(UIStore).isRequired, | 193 | ui: PropTypes.instanceOf(UIStore).isRequired, |
194 | }).isRequired, | 194 | }).isRequired, |
195 | actions: PropTypes.shape({ | ||
196 | app: PropTypes.shape({ | ||
197 | openExternalUrl: PropTypes.func.isRequired, | ||
198 | }).isRequired, | ||
199 | }).isRequired, | ||
195 | }; | 200 | }; |
196 | 201 | ||
197 | static contextTypes = { | 202 | static contextTypes = { |
@@ -199,7 +204,7 @@ class AnnouncementScreen extends Component { | |||
199 | }; | 204 | }; |
200 | 205 | ||
201 | render() { | 206 | render() { |
202 | const { classes, stores } = this.props; | 207 | const { classes, stores, actions } = this.props; |
203 | const { intl } = this.context; | 208 | const { intl } = this.context; |
204 | const { changelog, announcement } = announcementsStore; | 209 | const { changelog, announcement } = announcementsStore; |
205 | const themeImage = stores.ui.isDarkThemeActive ? 'dark' : 'light'; | 210 | const themeImage = stores.ui.isDarkThemeActive ? 'dark' : 'light'; |
@@ -223,14 +228,23 @@ class AnnouncementScreen extends Component { | |||
223 | __html: marked(announcement.main.text, markedOptions), | 228 | __html: marked(announcement.main.text, markedOptions), |
224 | }} | 229 | }} |
225 | /> | 230 | /> |
226 | <div className={classes.mainCtaButton}> | 231 | {(announcement.main.cta.label || announcement.main.cta.href) && ( |
227 | <Button | 232 | <div className={classes.mainCtaButton}> |
228 | label={announcement.main.cta.label} | 233 | <Button |
229 | onClick={() => { | 234 | label={announcement.main.cta.label} |
230 | window.location.href = `#${announcement.main.cta.href}`; | 235 | onClick={() => { |
231 | }} | 236 | const { |
232 | /> | 237 | href, |
233 | </div> | 238 | } = announcement.main.cta; |
239 | if (announcement.main.cta.href.startsWith('http')) { | ||
240 | actions.app.openExternalUrl({ url: href }); | ||
241 | } else { | ||
242 | window.location.href = `#${href}`; | ||
243 | } | ||
244 | }} | ||
245 | /> | ||
246 | </div> | ||
247 | )} | ||
234 | </div> | 248 | </div> |
235 | </div> | 249 | </div> |
236 | </div> | 250 | </div> |
@@ -250,7 +264,14 @@ class AnnouncementScreen extends Component { | |||
250 | <Button | 264 | <Button |
251 | label={announcement.spotlight.cta.label} | 265 | label={announcement.spotlight.cta.label} |
252 | onClick={() => { | 266 | onClick={() => { |
253 | window.location.href = `#${announcement.spotlight.cta.href}`; | 267 | const { |
268 | href, | ||
269 | } = announcement.spotlight.cta; | ||
270 | if (announcement.spotlight.cta.href.startsWith('http')) { | ||
271 | actions.app.openExternalUrl({ url: href }); | ||
272 | } else { | ||
273 | window.location.href = `#${href}`; | ||
274 | } | ||
254 | }} | 275 | }} |
255 | /> | 276 | /> |
256 | </div> | 277 | </div> |
diff --git a/src/features/planSelection/components/PlanItem.js b/src/features/planSelection/components/PlanItem.js index ec061377b..3855fedf1 100644 --- a/src/features/planSelection/components/PlanItem.js +++ b/src/features/planSelection/components/PlanItem.js | |||
@@ -49,6 +49,7 @@ const styles = theme => ({ | |||
49 | priceWrapper: { | 49 | priceWrapper: { |
50 | height: 50, | 50 | height: 50, |
51 | marginBottom: 0, | 51 | marginBottom: 0, |
52 | marginTop: ({ text }) => (!text ? 15 : 0), | ||
52 | }, | 53 | }, |
53 | price: { | 54 | price: { |
54 | fontSize: 50, | 55 | fontSize: 50, |
@@ -64,7 +65,7 @@ const styles = theme => ({ | |||
64 | cta: { | 65 | cta: { |
65 | background: theme.styleTypes.primary.accent, | 66 | background: theme.styleTypes.primary.accent, |
66 | color: theme.styleTypes.primary.contrast, | 67 | color: theme.styleTypes.primary.contrast, |
67 | margin: [40, 'auto', 0, 'auto'], | 68 | margin: [30, 'auto', 0, 'auto'], |
68 | }, | 69 | }, |
69 | divider: { | 70 | divider: { |
70 | width: 40, | 71 | width: 40, |
@@ -77,10 +78,14 @@ const styles = theme => ({ | |||
77 | background: color(theme.styleTypes.primary.accent).darken(0.25).hex(), | 78 | background: color(theme.styleTypes.primary.accent).darken(0.25).hex(), |
78 | color: theme.styleTypes.primary.contrast, | 79 | color: theme.styleTypes.primary.contrast, |
79 | position: 'relative', | 80 | position: 'relative', |
81 | height: 'auto', | ||
80 | }, | 82 | }, |
81 | content: { | 83 | content: { |
82 | padding: [10, 20, 20], | 84 | padding: [10, 20, 20], |
83 | background: '#EFEFEF', | 85 | background: '#EFEFEF', |
86 | display: 'flex', | ||
87 | flexDirection: 'column', | ||
88 | justifyContent: 'space-between', | ||
84 | }, | 89 | }, |
85 | simpleCTA: { | 90 | simpleCTA: { |
86 | background: 'none', | 91 | background: 'none', |
@@ -167,10 +172,14 @@ export default @observer @injectSheet(styles) class PlanItem extends Component { | |||
167 | </div> | 172 | </div> |
168 | )} | 173 | )} |
169 | <H2 className={classes.planName}>{name}</H2> | 174 | <H2 className={classes.planName}>{name}</H2> |
170 | <p className={classes.text}> | 175 | {text && ( |
171 | {text} | 176 | <> |
172 | </p> | 177 | <p className={classes.text}> |
173 | <hr className={classes.divider} /> | 178 | {text} |
179 | </p> | ||
180 | <hr className={classes.divider} /> | ||
181 | </> | ||
182 | )} | ||
174 | <p className={classes.priceWrapper}> | 183 | <p className={classes.priceWrapper}> |
175 | <span className={classes.currency}>{currency}</span> | 184 | <span className={classes.currency}>{currency}</span> |
176 | <span className={classes.price}> | 185 | <span className={classes.price}> |
diff --git a/src/features/planSelection/components/PlanSelection.js b/src/features/planSelection/components/PlanSelection.js index 4bf5238dd..6f0dd30ad 100644 --- a/src/features/planSelection/components/PlanSelection.js +++ b/src/features/planSelection/components/PlanSelection.js | |||
@@ -6,7 +6,7 @@ import { defineMessages, intlShape } from 'react-intl'; | |||
6 | import { H1, H2, Icon } from '@meetfranz/ui'; | 6 | import { H1, H2, Icon } from '@meetfranz/ui'; |
7 | import color from 'color'; | 7 | import color from 'color'; |
8 | 8 | ||
9 | import { mdiRocket, mdiArrowRight } from '@mdi/js'; | 9 | import { mdiArrowRight } from '@mdi/js'; |
10 | import PlanItem from './PlanItem'; | 10 | import PlanItem from './PlanItem'; |
11 | import { i18nPlanName } from '../../../helpers/plan-helpers'; | 11 | import { i18nPlanName } from '../../../helpers/plan-helpers'; |
12 | import { PLANS } from '../../../config'; | 12 | import { PLANS } from '../../../config'; |
@@ -79,10 +79,10 @@ const styles = theme => ({ | |||
79 | overflowY: 'scroll', | 79 | overflowY: 'scroll', |
80 | }, | 80 | }, |
81 | container: { | 81 | container: { |
82 | width: '80%', | 82 | // width: '80%', |
83 | height: 'auto', | 83 | height: 'auto', |
84 | background: theme.styleTypes.primary.accent, | 84 | // background: theme.styleTypes.primary.accent, |
85 | padding: 40, | 85 | // padding: 40, |
86 | borderRadius: theme.borderRadius, | 86 | borderRadius: theme.borderRadius, |
87 | maxWidth: 1000, | 87 | maxWidth: 1000, |
88 | 88 | ||
@@ -104,23 +104,6 @@ const styles = theme => ({ | |||
104 | boxShadow: [0, 2, 30, color('#000').alpha(0.1).rgb().string()], | 104 | boxShadow: [0, 2, 30, color('#000').alpha(0.1).rgb().string()], |
105 | }, | 105 | }, |
106 | }, | 106 | }, |
107 | bigIcon: { | ||
108 | background: theme.styleTypes.danger.accent, | ||
109 | width: 120, | ||
110 | height: 120, | ||
111 | display: 'flex', | ||
112 | alignItems: 'center', | ||
113 | borderRadius: '100%', | ||
114 | justifyContent: 'center', | ||
115 | margin: [-100, 'auto', 20], | ||
116 | |||
117 | '& svg': { | ||
118 | width: '80px !important', | ||
119 | height: '80px !important', | ||
120 | filter: 'drop-shadow( 0px 2px 3px rgba(0, 0, 0, 0.3))', | ||
121 | fill: theme.styleTypes.danger.contrast, | ||
122 | }, | ||
123 | }, | ||
124 | headline: { | 107 | headline: { |
125 | fontSize: 40, | 108 | fontSize: 40, |
126 | }, | 109 | }, |
@@ -158,7 +141,7 @@ const styles = theme => ({ | |||
158 | overflow: 'scroll-x', | 141 | overflow: 'scroll-x', |
159 | }, | 142 | }, |
160 | featuredPlan: { | 143 | featuredPlan: { |
161 | transform: 'scale(1.05)', | 144 | transform: ({ isPersonalPlanAvailable }) => (isPersonalPlanAvailable ? 'scale(1.05)' : null), |
162 | }, | 145 | }, |
163 | disclaimer: { | 146 | disclaimer: { |
164 | textAlign: 'right', | 147 | textAlign: 'right', |
@@ -177,8 +160,13 @@ class PlanSelection extends Component { | |||
177 | upgradeAccount: PropTypes.func.isRequired, | 160 | upgradeAccount: PropTypes.func.isRequired, |
178 | stayOnFree: PropTypes.func.isRequired, | 161 | stayOnFree: PropTypes.func.isRequired, |
179 | hadSubscription: PropTypes.bool.isRequired, | 162 | hadSubscription: PropTypes.bool.isRequired, |
163 | isPersonalPlanAvailable: PropTypes.bool, | ||
180 | }; | 164 | }; |
181 | 165 | ||
166 | static defaultProps = { | ||
167 | isPersonalPlanAvailable: true, | ||
168 | } | ||
169 | |||
182 | static contextTypes = { | 170 | static contextTypes = { |
183 | intl: intlShape, | 171 | intl: intlShape, |
184 | }; | 172 | }; |
@@ -196,6 +184,7 @@ class PlanSelection extends Component { | |||
196 | upgradeAccount, | 184 | upgradeAccount, |
197 | stayOnFree, | 185 | stayOnFree, |
198 | hadSubscription, | 186 | hadSubscription, |
187 | isPersonalPlanAvailable, | ||
199 | } = this.props; | 188 | } = this.props; |
200 | 189 | ||
201 | const { intl } = this.context; | 190 | const { intl } = this.context; |
@@ -206,15 +195,14 @@ class PlanSelection extends Component { | |||
206 | className={classes.root} | 195 | className={classes.root} |
207 | > | 196 | > |
208 | <div className={classes.container}> | 197 | <div className={classes.container}> |
209 | <div className={classes.bigIcon}> | ||
210 | <Icon icon={mdiRocket} /> | ||
211 | </div> | ||
212 | <H1 className={classes.headline}>{intl.formatMessage(messages.welcome, { name: firstname })}</H1> | 198 | <H1 className={classes.headline}>{intl.formatMessage(messages.welcome, { name: firstname })}</H1> |
213 | <H2 className={classes.subheadline}>{intl.formatMessage(messages.subheadline)}</H2> | 199 | {isPersonalPlanAvailable && ( |
200 | <H2 className={classes.subheadline}>{intl.formatMessage(messages.subheadline)}</H2> | ||
201 | )} | ||
214 | <div className={classes.plans}> | 202 | <div className={classes.plans}> |
215 | <PlanItem | 203 | <PlanItem |
216 | name={i18nPlanName(PLANS.FREE, intl)} | 204 | name={i18nPlanName(PLANS.FREE, intl)} |
217 | text={intl.formatMessage(messages.textFree)} | 205 | text={isPersonalPlanAvailable ? intl.formatMessage(messages.textFree) : null} |
218 | price={0} | 206 | price={0} |
219 | currency={currency} | 207 | currency={currency} |
220 | ctaLabel={intl.formatMessage(subscriptionExpired ? messages.ctaDowngradeFree : messages.ctaStayOnFree)} | 208 | ctaLabel={intl.formatMessage(subscriptionExpired ? messages.ctaDowngradeFree : messages.ctaStayOnFree)} |
@@ -228,33 +216,35 @@ class PlanSelection extends Component { | |||
228 | </PlanItem> | 216 | </PlanItem> |
229 | <PlanItem | 217 | <PlanItem |
230 | name={i18nPlanName(plans.pro.yearly.id, intl)} | 218 | name={i18nPlanName(plans.pro.yearly.id, intl)} |
231 | text={intl.formatMessage(messages.textProfessional)} | 219 | text={isPersonalPlanAvailable ? intl.formatMessage(messages.textProfessional) : null} |
232 | price={plans.pro.yearly.price} | 220 | price={plans.pro.yearly.price} |
233 | currency={currency} | 221 | currency={currency} |
234 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPro : messages.actionTrial)} | 222 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPro : messages.actionTrial)} |
235 | upgrade={() => upgradeAccount(plans.pro.yearly.id)} | 223 | upgrade={() => upgradeAccount(plans.pro.yearly.id)} |
236 | className={classes.featuredPlan} | 224 | className={classes.featuredPlan} |
237 | perUser | 225 | perUser |
238 | bestValue | 226 | bestValue={isPersonalPlanAvailable} |
239 | > | ||
240 | <FeatureList | ||
241 | plan={PLANS.PRO} | ||
242 | className={classes.featureList} | ||
243 | /> | ||
244 | </PlanItem> | ||
245 | <PlanItem | ||
246 | name={i18nPlanName(plans.personal.yearly.id, intl)} | ||
247 | text={intl.formatMessage(messages.textPersonal)} | ||
248 | price={plans.personal.yearly.price} | ||
249 | currency={currency} | ||
250 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPersonal : messages.actionTrial)} | ||
251 | upgrade={() => upgradeAccount(plans.personal.yearly.id)} | ||
252 | > | 227 | > |
253 | <FeatureList | 228 | <FeatureList |
254 | plan={PLANS.PERSONAL} | 229 | plan={isPersonalPlanAvailable ? PLANS.PRO : null} |
255 | className={classes.featureList} | 230 | className={classes.featureList} |
256 | /> | 231 | /> |
257 | </PlanItem> | 232 | </PlanItem> |
233 | {isPersonalPlanAvailable && ( | ||
234 | <PlanItem | ||
235 | name={i18nPlanName(plans.personal.yearly.id, intl)} | ||
236 | text={intl.formatMessage(messages.textPersonal)} | ||
237 | price={plans.personal.yearly.price} | ||
238 | currency={currency} | ||
239 | ctaLabel={intl.formatMessage(hadSubscription ? messages.shortActionPersonal : messages.actionTrial)} | ||
240 | upgrade={() => upgradeAccount(plans.personal.yearly.id)} | ||
241 | > | ||
242 | <FeatureList | ||
243 | plan={PLANS.PERSONAL} | ||
244 | className={classes.featureList} | ||
245 | /> | ||
246 | </PlanItem> | ||
247 | )} | ||
258 | </div> | 248 | </div> |
259 | <div className={classes.footer}> | 249 | <div className={classes.footer}> |
260 | <a | 250 | <a |
diff --git a/src/features/planSelection/containers/PlanSelectionScreen.js b/src/features/planSelection/containers/PlanSelectionScreen.js index d202c924e..e4d85cda5 100644 --- a/src/features/planSelection/containers/PlanSelectionScreen.js +++ b/src/features/planSelection/containers/PlanSelectionScreen.js | |||
@@ -53,7 +53,8 @@ class PlanSelectionScreen extends Component { | |||
53 | const { intl } = this.context; | 53 | const { intl } = this.context; |
54 | 54 | ||
55 | const { user, features } = this.props.stores; | 55 | const { user, features } = this.props.stores; |
56 | const { plans, currency } = features.features.pricingConfig; | 56 | const { isPersonalPlanAvailable, pricingConfig } = features.features; |
57 | const { plans, currency } = pricingConfig; | ||
57 | const { activateTrial } = this.props.actions.user; | 58 | const { activateTrial } = this.props.actions.user; |
58 | const { downgradeAccount, hideOverlay } = this.props.actions.planSelection; | 59 | const { downgradeAccount, hideOverlay } = this.props.actions.planSelection; |
59 | 60 | ||
@@ -95,6 +96,7 @@ class PlanSelectionScreen extends Component { | |||
95 | }} | 96 | }} |
96 | subscriptionExpired={user.team && user.team.state === 'expired' && !user.team.userHasDowngraded} | 97 | subscriptionExpired={user.team && user.team.state === 'expired' && !user.team.userHasDowngraded} |
97 | hadSubscription={user.data.hadSubscription} | 98 | hadSubscription={user.data.hadSubscription} |
99 | isPersonalPlanAvailable={isPersonalPlanAvailable} | ||
98 | /> | 100 | /> |
99 | </ErrorBoundary> | 101 | </ErrorBoundary> |
100 | ); | 102 | ); |
diff --git a/src/helpers/plan-helpers.js b/src/helpers/plan-helpers.js index ee22e4471..b474f8bbd 100644 --- a/src/helpers/plan-helpers.js +++ b/src/helpers/plan-helpers.js | |||
@@ -20,6 +20,10 @@ const messages = defineMessages({ | |||
20 | }, | 20 | }, |
21 | }); | 21 | }); |
22 | 22 | ||
23 | export function cleanupPlanId(id) { | ||
24 | return id.replace(/(.*)-x[0-9]/, '$1'); | ||
25 | } | ||
26 | |||
23 | export function i18nPlanName(planId, intl) { | 27 | export function i18nPlanName(planId, intl) { |
24 | if (!planId) { | 28 | if (!planId) { |
25 | throw new Error('planId is required'); | 29 | throw new Error('planId is required'); |
@@ -29,7 +33,9 @@ export function i18nPlanName(planId, intl) { | |||
29 | throw new Error('intl context is required'); | 33 | throw new Error('intl context is required'); |
30 | } | 34 | } |
31 | 35 | ||
32 | const plan = PLANS_MAPPING[planId]; | 36 | const id = cleanupPlanId(planId); |
37 | |||
38 | const plan = PLANS_MAPPING[id]; | ||
33 | 39 | ||
34 | return intl.formatMessage(messages[plan]); | 40 | return intl.formatMessage(messages[plan]); |
35 | } | 41 | } |
@@ -39,7 +45,9 @@ export function getPlan(planId) { | |||
39 | throw new Error('planId is required'); | 45 | throw new Error('planId is required'); |
40 | } | 46 | } |
41 | 47 | ||
42 | const plan = PLANS_MAPPING[planId]; | 48 | const id = cleanupPlanId(planId); |
49 | |||
50 | const plan = PLANS_MAPPING[id]; | ||
43 | 51 | ||
44 | return plan; | 52 | return plan; |
45 | } | 53 | } |
diff --git a/src/helpers/userAgent-helpers.js b/src/helpers/userAgent-helpers.js new file mode 100644 index 000000000..15edc1054 --- /dev/null +++ b/src/helpers/userAgent-helpers.js | |||
@@ -0,0 +1,45 @@ | |||
1 | import { remote, app } from 'electron'; | ||
2 | import os from 'os'; | ||
3 | import macosVersion from 'macos-version'; | ||
4 | import { isMac, isWindows } from '../environment'; | ||
5 | |||
6 | // This helper gets included from the backend and frontend but we only need to use "remote" | ||
7 | // if we are in the frontend | ||
8 | const ferdiVersion = remote && remote.app ? remote.app.getVersion() : app.getVersion(); | ||
9 | |||
10 | function macOS() { | ||
11 | const version = macosVersion(); | ||
12 | |||
13 | return `Macintosh; Intel Mac OS X ${version.replace(/\./g, '_')}`; | ||
14 | } | ||
15 | |||
16 | function windows() { | ||
17 | const version = os.release(); | ||
18 | const [majorVersion, minorVersion] = version.split('.'); | ||
19 | return `Windows NT ${majorVersion}.${minorVersion}; Win64; x64`; | ||
20 | } | ||
21 | |||
22 | function linux() { | ||
23 | return 'X11; Ubuntu; Linux x86_64'; | ||
24 | } | ||
25 | |||
26 | export default function userAgent(removeChromeVersion = false) { | ||
27 | let platformString = ''; | ||
28 | |||
29 | if (isMac) { | ||
30 | platformString = macOS(); | ||
31 | } else if (isWindows) { | ||
32 | platformString = windows(); | ||
33 | } else { | ||
34 | platformString = linux(); | ||
35 | } | ||
36 | |||
37 | let applicationString = ''; | ||
38 | if (!removeChromeVersion) { | ||
39 | applicationString = ` Ferdi/${ferdiVersion} (Electron ${process.versions.electron})`; | ||
40 | } | ||
41 | |||
42 | // TODO: Update AppleWebKit and Safari version after electron update | ||
43 | return `Mozilla/5.0 (${platformString}) AppleWebKit/537.36 (KHTML, like Gecko) Chrome${!removeChromeVersion ? `/${process.versions.chrome}` : ''} Safari/537.36${applicationString}`; | ||
44 | // Mozilla/5.0 (Macintosh; Intel Mac OS X 10_14_6) AppleWebKit/537.36 (KHTML, like Gecko) old-airport-include/1.0.0 Chrome Electron/7.1.7 Safari/537.36 | ||
45 | } | ||
diff --git a/src/i18n/locales/defaultMessages.json b/src/i18n/locales/defaultMessages.json index 56d0b1971..f3ff7caf3 100644 --- a/src/i18n/locales/defaultMessages.json +++ b/src/i18n/locales/defaultMessages.json | |||
@@ -1193,6 +1193,190 @@ | |||
1193 | { | 1193 | { |
1194 | "descriptors": [ | 1194 | "descriptors": [ |
1195 | { | 1195 | { |
1196 | "defaultMessage": "!!!Home", | ||
1197 | "end": { | ||
1198 | "column": 3, | ||
1199 | "line": 16 | ||
1200 | }, | ||
1201 | "file": "src/components/services/content/ConnectionBanner.js", | ||
1202 | "id": "webControls.goHome", | ||
1203 | "start": { | ||
1204 | "column": 10, | ||
1205 | "line": 13 | ||
1206 | } | ||
1207 | }, | ||
1208 | { | ||
1209 | "defaultMessage": "!!!Open in Browser", | ||
1210 | "end": { | ||
1211 | "column": 3, | ||
1212 | "line": 20 | ||
1213 | }, | ||
1214 | "file": "src/components/services/content/ConnectionBanner.js", | ||
1215 | "id": "webControls.openInBrowser", | ||
1216 | "start": { | ||
1217 | "column": 17, | ||
1218 | "line": 17 | ||
1219 | } | ||
1220 | }, | ||
1221 | { | ||
1222 | "defaultMessage": "!!!Back", | ||
1223 | "end": { | ||
1224 | "column": 3, | ||
1225 | "line": 24 | ||
1226 | }, | ||
1227 | "file": "src/components/services/content/ConnectionBanner.js", | ||
1228 | "id": "webControls.back", | ||
1229 | "start": { | ||
1230 | "column": 8, | ||
1231 | "line": 21 | ||
1232 | } | ||
1233 | }, | ||
1234 | { | ||
1235 | "defaultMessage": "!!!Forward", | ||
1236 | "end": { | ||
1237 | "column": 3, | ||
1238 | "line": 28 | ||
1239 | }, | ||
1240 | "file": "src/components/services/content/ConnectionBanner.js", | ||
1241 | "id": "webControls.forward", | ||
1242 | "start": { | ||
1243 | "column": 11, | ||
1244 | "line": 25 | ||
1245 | } | ||
1246 | }, | ||
1247 | { | ||
1248 | "defaultMessage": "!!!Reload", | ||
1249 | "end": { | ||
1250 | "column": 3, | ||
1251 | "line": 32 | ||
1252 | }, | ||
1253 | "file": "src/components/services/content/ConnectionBanner.js", | ||
1254 | "id": "webControls.reload", | ||
1255 | "start": { | ||
1256 | "column": 10, | ||
1257 | "line": 29 | ||
1258 | } | ||
1259 | } | ||
1260 | ], | ||
1261 | "path": "src/components/services/content/ConnectionBanner.json" | ||
1262 | }, | ||
1263 | { | ||
1264 | "descriptors": [ | ||
1265 | { | ||
1266 | "defaultMessage": "!!!Home", | ||
1267 | "end": { | ||
1268 | "column": 3, | ||
1269 | "line": 16 | ||
1270 | }, | ||
1271 | "file": "src/components/services/content/ConnectionLost.js", | ||
1272 | "id": "webControls.goHome", | ||
1273 | "start": { | ||
1274 | "column": 10, | ||
1275 | "line": 13 | ||
1276 | } | ||
1277 | }, | ||
1278 | { | ||
1279 | "defaultMessage": "!!!Open in Browser", | ||
1280 | "end": { | ||
1281 | "column": 3, | ||
1282 | "line": 20 | ||
1283 | }, | ||
1284 | "file": "src/components/services/content/ConnectionLost.js", | ||
1285 | "id": "webControls.openInBrowser", | ||
1286 | "start": { | ||
1287 | "column": 17, | ||
1288 | "line": 17 | ||
1289 | } | ||
1290 | }, | ||
1291 | { | ||
1292 | "defaultMessage": "!!!Back", | ||
1293 | "end": { | ||
1294 | "column": 3, | ||
1295 | "line": 24 | ||
1296 | }, | ||
1297 | "file": "src/components/services/content/ConnectionLost.js", | ||
1298 | "id": "webControls.back", | ||
1299 | "start": { | ||
1300 | "column": 8, | ||
1301 | "line": 21 | ||
1302 | } | ||
1303 | }, | ||
1304 | { | ||
1305 | "defaultMessage": "!!!Forward", | ||
1306 | "end": { | ||
1307 | "column": 3, | ||
1308 | "line": 28 | ||
1309 | }, | ||
1310 | "file": "src/components/services/content/ConnectionLost.js", | ||
1311 | "id": "webControls.forward", | ||
1312 | "start": { | ||
1313 | "column": 11, | ||
1314 | "line": 25 | ||
1315 | } | ||
1316 | }, | ||
1317 | { | ||
1318 | "defaultMessage": "!!!Reload", | ||
1319 | "end": { | ||
1320 | "column": 3, | ||
1321 | "line": 32 | ||
1322 | }, | ||
1323 | "file": "src/components/services/content/ConnectionLost.js", | ||
1324 | "id": "webControls.reload", | ||
1325 | "start": { | ||
1326 | "column": 10, | ||
1327 | "line": 29 | ||
1328 | } | ||
1329 | } | ||
1330 | ], | ||
1331 | "path": "src/components/services/content/ConnectionLost.json" | ||
1332 | }, | ||
1333 | { | ||
1334 | "descriptors": [ | ||
1335 | { | ||
1336 | "defaultMessage": "!!!Oh no! Franz lost the connection to {name}.", | ||
1337 | "end": { | ||
1338 | "column": 3, | ||
1339 | "line": 18 | ||
1340 | }, | ||
1341 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
1342 | "id": "connectionLostBanner.message", | ||
1343 | "start": { | ||
1344 | "column": 8, | ||
1345 | "line": 15 | ||
1346 | } | ||
1347 | }, | ||
1348 | { | ||
1349 | "defaultMessage": "!!!What happened?", | ||
1350 | "end": { | ||
1351 | "column": 3, | ||
1352 | "line": 22 | ||
1353 | }, | ||
1354 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
1355 | "id": "connectionLostBanner.informationLink", | ||
1356 | "start": { | ||
1357 | "column": 19, | ||
1358 | "line": 19 | ||
1359 | } | ||
1360 | }, | ||
1361 | { | ||
1362 | "defaultMessage": "!!!Reload Service", | ||
1363 | "end": { | ||
1364 | "column": 3, | ||
1365 | "line": 26 | ||
1366 | }, | ||
1367 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
1368 | "id": "connectionLostBanner.cta", | ||
1369 | "start": { | ||
1370 | "column": 7, | ||
1371 | "line": 23 | ||
1372 | } | ||
1373 | } | ||
1374 | ], | ||
1375 | "path": "src/components/services/content/ConnectionLostBanner.json" | ||
1376 | }, | ||
1377 | { | ||
1378 | "descriptors": [ | ||
1379 | { | ||
1196 | "defaultMessage": "!!!Oh no!", | 1380 | "defaultMessage": "!!!Oh no!", |
1197 | "end": { | 1381 | "end": { |
1198 | "column": 3, | 1382 | "column": 3, |
@@ -1434,6 +1618,76 @@ | |||
1434 | { | 1618 | { |
1435 | "descriptors": [ | 1619 | "descriptors": [ |
1436 | { | 1620 | { |
1621 | "defaultMessage": "!!!Home", | ||
1622 | "end": { | ||
1623 | "column": 3, | ||
1624 | "line": 16 | ||
1625 | }, | ||
1626 | "file": "src/components/services/content/WebControls.js", | ||
1627 | "id": "webControls.goHome", | ||
1628 | "start": { | ||
1629 | "column": 10, | ||
1630 | "line": 13 | ||
1631 | } | ||
1632 | }, | ||
1633 | { | ||
1634 | "defaultMessage": "!!!Open in Browser", | ||
1635 | "end": { | ||
1636 | "column": 3, | ||
1637 | "line": 20 | ||
1638 | }, | ||
1639 | "file": "src/components/services/content/WebControls.js", | ||
1640 | "id": "webControls.openInBrowser", | ||
1641 | "start": { | ||
1642 | "column": 17, | ||
1643 | "line": 17 | ||
1644 | } | ||
1645 | }, | ||
1646 | { | ||
1647 | "defaultMessage": "!!!Back", | ||
1648 | "end": { | ||
1649 | "column": 3, | ||
1650 | "line": 24 | ||
1651 | }, | ||
1652 | "file": "src/components/services/content/WebControls.js", | ||
1653 | "id": "webControls.back", | ||
1654 | "start": { | ||
1655 | "column": 8, | ||
1656 | "line": 21 | ||
1657 | } | ||
1658 | }, | ||
1659 | { | ||
1660 | "defaultMessage": "!!!Forward", | ||
1661 | "end": { | ||
1662 | "column": 3, | ||
1663 | "line": 28 | ||
1664 | }, | ||
1665 | "file": "src/components/services/content/WebControls.js", | ||
1666 | "id": "webControls.forward", | ||
1667 | "start": { | ||
1668 | "column": 11, | ||
1669 | "line": 25 | ||
1670 | } | ||
1671 | }, | ||
1672 | { | ||
1673 | "defaultMessage": "!!!Reload", | ||
1674 | "end": { | ||
1675 | "column": 3, | ||
1676 | "line": 32 | ||
1677 | }, | ||
1678 | "file": "src/components/services/content/WebControls.js", | ||
1679 | "id": "webControls.reload", | ||
1680 | "start": { | ||
1681 | "column": 10, | ||
1682 | "line": 29 | ||
1683 | } | ||
1684 | } | ||
1685 | ], | ||
1686 | "path": "src/components/services/content/WebControls.json" | ||
1687 | }, | ||
1688 | { | ||
1689 | "descriptors": [ | ||
1690 | { | ||
1437 | "defaultMessage": "!!!Oh no!", | 1691 | "defaultMessage": "!!!Oh no!", |
1438 | "end": { | 1692 | "end": { |
1439 | "column": 3, | 1693 | "column": 3, |
@@ -2421,107 +2675,120 @@ | |||
2421 | } | 2675 | } |
2422 | }, | 2676 | }, |
2423 | { | 2677 | { |
2424 | "defaultMessage": "!!!Notifications", | 2678 | "defaultMessage": "!!!You currently have hibernation enabled but you can disable hibernation for individual services using this option.", |
2425 | "end": { | 2679 | "end": { |
2426 | "column": 3, | 2680 | "column": 3, |
2427 | "line": 99 | 2681 | "line": 99 |
2428 | }, | 2682 | }, |
2429 | "file": "src/components/settings/services/EditServiceForm.js", | 2683 | "file": "src/components/settings/services/EditServiceForm.js", |
2684 | "id": "settings.service.form.disableHibernationInfo", | ||
2685 | "start": { | ||
2686 | "column": 26, | ||
2687 | "line": 96 | ||
2688 | } | ||
2689 | }, | ||
2690 | { | ||
2691 | "defaultMessage": "!!!Notifications", | ||
2692 | "end": { | ||
2693 | "column": 3, | ||
2694 | "line": 103 | ||
2695 | }, | ||
2696 | "file": "src/components/settings/services/EditServiceForm.js", | ||
2430 | "id": "settings.service.form.headlineNotifications", | 2697 | "id": "settings.service.form.headlineNotifications", |
2431 | "start": { | 2698 | "start": { |
2432 | "column": 25, | 2699 | "column": 25, |
2433 | "line": 96 | 2700 | "line": 100 |
2434 | } | 2701 | } |
2435 | }, | 2702 | }, |
2436 | { | 2703 | { |
2437 | "defaultMessage": "!!!Unread message badges", | 2704 | "defaultMessage": "!!!Unread message badges", |
2438 | "end": { | 2705 | "end": { |
2439 | "column": 3, | 2706 | "column": 3, |
2440 | "line": 103 | 2707 | "line": 107 |
2441 | }, | 2708 | }, |
2442 | "file": "src/components/settings/services/EditServiceForm.js", | 2709 | "file": "src/components/settings/services/EditServiceForm.js", |
2443 | "id": "settings.service.form.headlineBadges", | 2710 | "id": "settings.service.form.headlineBadges", |
2444 | "start": { | 2711 | "start": { |
2445 | "column": 18, | 2712 | "column": 18, |
2446 | "line": 100 | 2713 | "line": 104 |
2447 | } | 2714 | } |
2448 | }, | 2715 | }, |
2449 | { | 2716 | { |
2450 | "defaultMessage": "!!!General", | 2717 | "defaultMessage": "!!!General", |
2451 | "end": { | 2718 | "end": { |
2452 | "column": 3, | 2719 | "column": 3, |
2453 | "line": 107 | 2720 | "line": 111 |
2454 | }, | 2721 | }, |
2455 | "file": "src/components/settings/services/EditServiceForm.js", | 2722 | "file": "src/components/settings/services/EditServiceForm.js", |
2456 | "id": "settings.service.form.headlineGeneral", | 2723 | "id": "settings.service.form.headlineGeneral", |
2457 | "start": { | 2724 | "start": { |
2458 | "column": 19, | 2725 | "column": 19, |
2459 | "line": 104 | 2726 | "line": 108 |
2460 | } | 2727 | } |
2461 | }, | 2728 | }, |
2462 | { | 2729 | { |
2463 | "defaultMessage": "!!!Delete", | 2730 | "defaultMessage": "!!!Delete", |
2464 | "end": { | 2731 | "end": { |
2465 | "column": 3, | 2732 | "column": 3, |
2466 | "line": 111 | 2733 | "line": 115 |
2467 | }, | 2734 | }, |
2468 | "file": "src/components/settings/services/EditServiceForm.js", | 2735 | "file": "src/components/settings/services/EditServiceForm.js", |
2469 | "id": "settings.service.form.iconDelete", | 2736 | "id": "settings.service.form.iconDelete", |
2470 | "start": { | 2737 | "start": { |
2471 | "column": 14, | 2738 | "column": 14, |
2472 | "line": 108 | 2739 | "line": 112 |
2473 | } | 2740 | } |
2474 | }, | 2741 | }, |
2475 | { | 2742 | { |
2476 | "defaultMessage": "!!!Drop your image, or click here", | 2743 | "defaultMessage": "!!!Drop your image, or click here", |
2477 | "end": { | 2744 | "end": { |
2478 | "column": 3, | 2745 | "column": 3, |
2479 | "line": 115 | 2746 | "line": 119 |
2480 | }, | 2747 | }, |
2481 | "file": "src/components/settings/services/EditServiceForm.js", | 2748 | "file": "src/components/settings/services/EditServiceForm.js", |
2482 | "id": "settings.service.form.iconUpload", | 2749 | "id": "settings.service.form.iconUpload", |
2483 | "start": { | 2750 | "start": { |
2484 | "column": 14, | 2751 | "column": 14, |
2485 | "line": 112 | 2752 | "line": 116 |
2486 | } | 2753 | } |
2487 | }, | 2754 | }, |
2488 | { | 2755 | { |
2489 | "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", | 2756 | "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", |
2490 | "end": { | 2757 | "end": { |
2491 | "column": 3, | 2758 | "column": 3, |
2492 | "line": 119 | 2759 | "line": 123 |
2493 | }, | 2760 | }, |
2494 | "file": "src/components/settings/services/EditServiceForm.js", | 2761 | "file": "src/components/settings/services/EditServiceForm.js", |
2495 | "id": "settings.service.form.proxy.headline", | 2762 | "id": "settings.service.form.proxy.headline", |
2496 | "start": { | 2763 | "start": { |
2497 | "column": 17, | 2764 | "column": 17, |
2498 | "line": 116 | 2765 | "line": 120 |
2499 | } | 2766 | } |
2500 | }, | 2767 | }, |
2501 | { | 2768 | { |
2502 | "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", | 2769 | "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", |
2503 | "end": { | 2770 | "end": { |
2504 | "column": 3, | 2771 | "column": 3, |
2505 | "line": 123 | 2772 | "line": 127 |
2506 | }, | 2773 | }, |
2507 | "file": "src/components/settings/services/EditServiceForm.js", | 2774 | "file": "src/components/settings/services/EditServiceForm.js", |
2508 | "id": "settings.service.form.proxy.restartInfo", | 2775 | "id": "settings.service.form.proxy.restartInfo", |
2509 | "start": { | 2776 | "start": { |
2510 | "column": 20, | 2777 | "column": 20, |
2511 | "line": 120 | 2778 | "line": 124 |
2512 | } | 2779 | } |
2513 | }, | 2780 | }, |
2514 | { | 2781 | { |
2515 | "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", | 2782 | "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", |
2516 | "end": { | 2783 | "end": { |
2517 | "column": 3, | 2784 | "column": 3, |
2518 | "line": 127 | 2785 | "line": 131 |
2519 | }, | 2786 | }, |
2520 | "file": "src/components/settings/services/EditServiceForm.js", | 2787 | "file": "src/components/settings/services/EditServiceForm.js", |
2521 | "id": "settings.service.form.proxy.info", | 2788 | "id": "settings.service.form.proxy.info", |
2522 | "start": { | 2789 | "start": { |
2523 | "column": 13, | 2790 | "column": 13, |
2524 | "line": 124 | 2791 | "line": 128 |
2525 | } | 2792 | } |
2526 | } | 2793 | } |
2527 | ], | 2794 | ], |
@@ -3888,6 +4155,19 @@ | |||
3888 | "column": 10, | 4155 | "column": 10, |
3889 | "line": 65 | 4156 | "line": 65 |
3890 | } | 4157 | } |
4158 | }, | ||
4159 | { | ||
4160 | "defaultMessage": "!!!Occasional Waiting Screens", | ||
4161 | "end": { | ||
4162 | "column": 3, | ||
4163 | "line": 72 | ||
4164 | }, | ||
4165 | "file": "src/components/ui/FeatureList.js", | ||
4166 | "id": "pricing.features.appDelaysEnabled", | ||
4167 | "start": { | ||
4168 | "column": 19, | ||
4169 | "line": 69 | ||
4170 | } | ||
3891 | } | 4171 | } |
3892 | ], | 4172 | ], |
3893 | "path": "src/components/ui/FeatureList.json" | 4173 | "path": "src/components/ui/FeatureList.json" |
@@ -4006,172 +4286,185 @@ | |||
4006 | } | 4286 | } |
4007 | }, | 4287 | }, |
4008 | { | 4288 | { |
4009 | "defaultMessage": "!!!Enable Notifications", | 4289 | "defaultMessage": "!!!Disable hibernation", |
4010 | "end": { | 4290 | "end": { |
4011 | "column": 3, | 4291 | "column": 3, |
4012 | "line": 39 | 4292 | "line": 39 |
4013 | }, | 4293 | }, |
4014 | "file": "src/containers/settings/EditServiceScreen.js", | 4294 | "file": "src/containers/settings/EditServiceScreen.js", |
4015 | "id": "settings.service.form.enableNotification", | 4295 | "id": "settings.service.form.disableHibernation", |
4016 | "start": { | 4296 | "start": { |
4017 | "column": 22, | 4297 | "column": 22, |
4018 | "line": 36 | 4298 | "line": 36 |
4019 | } | 4299 | } |
4020 | }, | 4300 | }, |
4021 | { | 4301 | { |
4022 | "defaultMessage": "!!!Show unread message badges", | 4302 | "defaultMessage": "!!!Enable Notifications", |
4023 | "end": { | 4303 | "end": { |
4024 | "column": 3, | 4304 | "column": 3, |
4025 | "line": 43 | 4305 | "line": 43 |
4026 | }, | 4306 | }, |
4027 | "file": "src/containers/settings/EditServiceScreen.js", | 4307 | "file": "src/containers/settings/EditServiceScreen.js", |
4308 | "id": "settings.service.form.enableNotification", | ||
4309 | "start": { | ||
4310 | "column": 22, | ||
4311 | "line": 40 | ||
4312 | } | ||
4313 | }, | ||
4314 | { | ||
4315 | "defaultMessage": "!!!Show unread message badges", | ||
4316 | "end": { | ||
4317 | "column": 3, | ||
4318 | "line": 47 | ||
4319 | }, | ||
4320 | "file": "src/containers/settings/EditServiceScreen.js", | ||
4028 | "id": "settings.service.form.enableBadge", | 4321 | "id": "settings.service.form.enableBadge", |
4029 | "start": { | 4322 | "start": { |
4030 | "column": 15, | 4323 | "column": 15, |
4031 | "line": 40 | 4324 | "line": 44 |
4032 | } | 4325 | } |
4033 | }, | 4326 | }, |
4034 | { | 4327 | { |
4035 | "defaultMessage": "!!!Enable audio", | 4328 | "defaultMessage": "!!!Enable audio", |
4036 | "end": { | 4329 | "end": { |
4037 | "column": 3, | 4330 | "column": 3, |
4038 | "line": 47 | 4331 | "line": 51 |
4039 | }, | 4332 | }, |
4040 | "file": "src/containers/settings/EditServiceScreen.js", | 4333 | "file": "src/containers/settings/EditServiceScreen.js", |
4041 | "id": "settings.service.form.enableAudio", | 4334 | "id": "settings.service.form.enableAudio", |
4042 | "start": { | 4335 | "start": { |
4043 | "column": 15, | 4336 | "column": 15, |
4044 | "line": 44 | 4337 | "line": 48 |
4045 | } | 4338 | } |
4046 | }, | 4339 | }, |
4047 | { | 4340 | { |
4048 | "defaultMessage": "!!!Team", | 4341 | "defaultMessage": "!!!Team", |
4049 | "end": { | 4342 | "end": { |
4050 | "column": 3, | 4343 | "column": 3, |
4051 | "line": 51 | 4344 | "line": 55 |
4052 | }, | 4345 | }, |
4053 | "file": "src/containers/settings/EditServiceScreen.js", | 4346 | "file": "src/containers/settings/EditServiceScreen.js", |
4054 | "id": "settings.service.form.team", | 4347 | "id": "settings.service.form.team", |
4055 | "start": { | 4348 | "start": { |
4056 | "column": 8, | 4349 | "column": 8, |
4057 | "line": 48 | 4350 | "line": 52 |
4058 | } | 4351 | } |
4059 | }, | 4352 | }, |
4060 | { | 4353 | { |
4061 | "defaultMessage": "!!!Service URL", | 4354 | "defaultMessage": "!!!Service URL", |
4062 | "end": { | 4355 | "end": { |
4063 | "column": 3, | 4356 | "column": 3, |
4064 | "line": 55 | 4357 | "line": 59 |
4065 | }, | 4358 | }, |
4066 | "file": "src/containers/settings/EditServiceScreen.js", | 4359 | "file": "src/containers/settings/EditServiceScreen.js", |
4067 | "id": "settings.service.form.customUrl", | 4360 | "id": "settings.service.form.customUrl", |
4068 | "start": { | 4361 | "start": { |
4069 | "column": 13, | 4362 | "column": 13, |
4070 | "line": 52 | 4363 | "line": 56 |
4071 | } | 4364 | } |
4072 | }, | 4365 | }, |
4073 | { | 4366 | { |
4074 | "defaultMessage": "!!!Show message badge for all new messages", | 4367 | "defaultMessage": "!!!Show message badge for all new messages", |
4075 | "end": { | 4368 | "end": { |
4076 | "column": 3, | 4369 | "column": 3, |
4077 | "line": 59 | 4370 | "line": 63 |
4078 | }, | 4371 | }, |
4079 | "file": "src/containers/settings/EditServiceScreen.js", | 4372 | "file": "src/containers/settings/EditServiceScreen.js", |
4080 | "id": "settings.service.form.indirectMessages", | 4373 | "id": "settings.service.form.indirectMessages", |
4081 | "start": { | 4374 | "start": { |
4082 | "column": 20, | 4375 | "column": 20, |
4083 | "line": 56 | 4376 | "line": 60 |
4084 | } | 4377 | } |
4085 | }, | 4378 | }, |
4086 | { | 4379 | { |
4087 | "defaultMessage": "!!!Custom icon", | 4380 | "defaultMessage": "!!!Custom icon", |
4088 | "end": { | 4381 | "end": { |
4089 | "column": 3, | 4382 | "column": 3, |
4090 | "line": 63 | 4383 | "line": 67 |
4091 | }, | 4384 | }, |
4092 | "file": "src/containers/settings/EditServiceScreen.js", | 4385 | "file": "src/containers/settings/EditServiceScreen.js", |
4093 | "id": "settings.service.form.icon", | 4386 | "id": "settings.service.form.icon", |
4094 | "start": { | 4387 | "start": { |
4095 | "column": 8, | 4388 | "column": 8, |
4096 | "line": 60 | 4389 | "line": 64 |
4097 | } | 4390 | } |
4098 | }, | 4391 | }, |
4099 | { | 4392 | { |
4100 | "defaultMessage": "!!!Enable Dark Mode", | 4393 | "defaultMessage": "!!!Enable Dark Mode", |
4101 | "end": { | 4394 | "end": { |
4102 | "column": 3, | 4395 | "column": 3, |
4103 | "line": 67 | 4396 | "line": 71 |
4104 | }, | 4397 | }, |
4105 | "file": "src/containers/settings/EditServiceScreen.js", | 4398 | "file": "src/containers/settings/EditServiceScreen.js", |
4106 | "id": "settings.service.form.enableDarkMode", | 4399 | "id": "settings.service.form.enableDarkMode", |
4107 | "start": { | 4400 | "start": { |
4108 | "column": 18, | 4401 | "column": 18, |
4109 | "line": 64 | 4402 | "line": 68 |
4110 | } | 4403 | } |
4111 | }, | 4404 | }, |
4112 | { | 4405 | { |
4113 | "defaultMessage": "!!!Use Proxy", | 4406 | "defaultMessage": "!!!Use Proxy", |
4114 | "end": { | 4407 | "end": { |
4115 | "column": 3, | 4408 | "column": 3, |
4116 | "line": 71 | 4409 | "line": 75 |
4117 | }, | 4410 | }, |
4118 | "file": "src/containers/settings/EditServiceScreen.js", | 4411 | "file": "src/containers/settings/EditServiceScreen.js", |
4119 | "id": "settings.service.form.proxy.isEnabled", | 4412 | "id": "settings.service.form.proxy.isEnabled", |
4120 | "start": { | 4413 | "start": { |
4121 | "column": 15, | 4414 | "column": 15, |
4122 | "line": 68 | 4415 | "line": 72 |
4123 | } | 4416 | } |
4124 | }, | 4417 | }, |
4125 | { | 4418 | { |
4126 | "defaultMessage": "!!!Proxy Host/IP", | 4419 | "defaultMessage": "!!!Proxy Host/IP", |
4127 | "end": { | 4420 | "end": { |
4128 | "column": 3, | 4421 | "column": 3, |
4129 | "line": 75 | 4422 | "line": 79 |
4130 | }, | 4423 | }, |
4131 | "file": "src/containers/settings/EditServiceScreen.js", | 4424 | "file": "src/containers/settings/EditServiceScreen.js", |
4132 | "id": "settings.service.form.proxy.host", | 4425 | "id": "settings.service.form.proxy.host", |
4133 | "start": { | 4426 | "start": { |
4134 | "column": 13, | 4427 | "column": 13, |
4135 | "line": 72 | 4428 | "line": 76 |
4136 | } | 4429 | } |
4137 | }, | 4430 | }, |
4138 | { | 4431 | { |
4139 | "defaultMessage": "!!!Port", | 4432 | "defaultMessage": "!!!Port", |
4140 | "end": { | 4433 | "end": { |
4141 | "column": 3, | 4434 | "column": 3, |
4142 | "line": 79 | 4435 | "line": 83 |
4143 | }, | 4436 | }, |
4144 | "file": "src/containers/settings/EditServiceScreen.js", | 4437 | "file": "src/containers/settings/EditServiceScreen.js", |
4145 | "id": "settings.service.form.proxy.port", | 4438 | "id": "settings.service.form.proxy.port", |
4146 | "start": { | 4439 | "start": { |
4147 | "column": 13, | 4440 | "column": 13, |
4148 | "line": 76 | 4441 | "line": 80 |
4149 | } | 4442 | } |
4150 | }, | 4443 | }, |
4151 | { | 4444 | { |
4152 | "defaultMessage": "!!!User", | 4445 | "defaultMessage": "!!!User", |
4153 | "end": { | 4446 | "end": { |
4154 | "column": 3, | 4447 | "column": 3, |
4155 | "line": 83 | 4448 | "line": 87 |
4156 | }, | 4449 | }, |
4157 | "file": "src/containers/settings/EditServiceScreen.js", | 4450 | "file": "src/containers/settings/EditServiceScreen.js", |
4158 | "id": "settings.service.form.proxy.user", | 4451 | "id": "settings.service.form.proxy.user", |
4159 | "start": { | 4452 | "start": { |
4160 | "column": 13, | 4453 | "column": 13, |
4161 | "line": 80 | 4454 | "line": 84 |
4162 | } | 4455 | } |
4163 | }, | 4456 | }, |
4164 | { | 4457 | { |
4165 | "defaultMessage": "!!!Password", | 4458 | "defaultMessage": "!!!Password", |
4166 | "end": { | 4459 | "end": { |
4167 | "column": 3, | 4460 | "column": 3, |
4168 | "line": 87 | 4461 | "line": 91 |
4169 | }, | 4462 | }, |
4170 | "file": "src/containers/settings/EditServiceScreen.js", | 4463 | "file": "src/containers/settings/EditServiceScreen.js", |
4171 | "id": "settings.service.form.proxy.password", | 4464 | "id": "settings.service.form.proxy.password", |
4172 | "start": { | 4465 | "start": { |
4173 | "column": 17, | 4466 | "column": 17, |
4174 | "line": 84 | 4467 | "line": 88 |
4175 | } | 4468 | } |
4176 | } | 4469 | } |
4177 | ], | 4470 | ], |
@@ -5327,6 +5620,76 @@ | |||
5327 | { | 5620 | { |
5328 | "descriptors": [ | 5621 | "descriptors": [ |
5329 | { | 5622 | { |
5623 | "defaultMessage": "!!!Home", | ||
5624 | "end": { | ||
5625 | "column": 3, | ||
5626 | "line": 16 | ||
5627 | }, | ||
5628 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
5629 | "id": "webControls.goHome", | ||
5630 | "start": { | ||
5631 | "column": 10, | ||
5632 | "line": 13 | ||
5633 | } | ||
5634 | }, | ||
5635 | { | ||
5636 | "defaultMessage": "!!!Open in Browser", | ||
5637 | "end": { | ||
5638 | "column": 3, | ||
5639 | "line": 20 | ||
5640 | }, | ||
5641 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
5642 | "id": "webControls.openInBrowser", | ||
5643 | "start": { | ||
5644 | "column": 17, | ||
5645 | "line": 17 | ||
5646 | } | ||
5647 | }, | ||
5648 | { | ||
5649 | "defaultMessage": "!!!Back", | ||
5650 | "end": { | ||
5651 | "column": 3, | ||
5652 | "line": 24 | ||
5653 | }, | ||
5654 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
5655 | "id": "webControls.back", | ||
5656 | "start": { | ||
5657 | "column": 8, | ||
5658 | "line": 21 | ||
5659 | } | ||
5660 | }, | ||
5661 | { | ||
5662 | "defaultMessage": "!!!Forward", | ||
5663 | "end": { | ||
5664 | "column": 3, | ||
5665 | "line": 28 | ||
5666 | }, | ||
5667 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
5668 | "id": "webControls.forward", | ||
5669 | "start": { | ||
5670 | "column": 11, | ||
5671 | "line": 25 | ||
5672 | } | ||
5673 | }, | ||
5674 | { | ||
5675 | "defaultMessage": "!!!Reload", | ||
5676 | "end": { | ||
5677 | "column": 3, | ||
5678 | "line": 32 | ||
5679 | }, | ||
5680 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
5681 | "id": "webControls.reload", | ||
5682 | "start": { | ||
5683 | "column": 10, | ||
5684 | "line": 29 | ||
5685 | } | ||
5686 | } | ||
5687 | ], | ||
5688 | "path": "src/features/recipeConnectionLost/components/WebControls.json" | ||
5689 | }, | ||
5690 | { | ||
5691 | "descriptors": [ | ||
5692 | { | ||
5330 | "defaultMessage": "!!!Changes in Franz {version}", | 5693 | "defaultMessage": "!!!Changes in Franz {version}", |
5331 | "end": { | 5694 | "end": { |
5332 | "column": 3, | 5695 | "column": 3, |
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 947f927e7..8a95970a2 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -4,6 +4,9 @@ | |||
4 | "changeserver.headline": "Change server", | 4 | "changeserver.headline": "Change server", |
5 | "changeserver.label": "Server", | 5 | "changeserver.label": "Server", |
6 | "changeserver.submit": "Submit", | 6 | "changeserver.submit": "Submit", |
7 | "connectionLostBanner.cta": "Reload Service", | ||
8 | "connectionLostBanner.informationLink": "What happened?", | ||
9 | "connectionLostBanner.message": "Oh no! Ferdi lost the connection to {name}.", | ||
7 | "feature.announcements.changelog.headline": "Changes in Ferdi {version}", | 10 | "feature.announcements.changelog.headline": "Changes in Ferdi {version}", |
8 | "feature.debugger.title": "Publish debugging information", | 11 | "feature.debugger.title": "Publish debugging information", |
9 | "feature.delayApp.headline": "Please purchase a Ferdi Supporter License to skip waiting", | 12 | "feature.delayApp.headline": "Please purchase a Ferdi Supporter License to skip waiting", |
@@ -183,6 +186,7 @@ | |||
183 | "pricing.features.accountSync": "Account Synchronisation", | 186 | "pricing.features.accountSync": "Account Synchronisation", |
184 | "pricing.features.adFree": "Forever ad-free", | 187 | "pricing.features.adFree": "Forever ad-free", |
185 | "pricing.features.appDelays": "No Waiting Screens", | 188 | "pricing.features.appDelays": "No Waiting Screens", |
189 | "pricing.features.appDelaysEnabled": "Occasional Waiting Screens", | ||
186 | "pricing.features.customWebsites": "Add Custom Websites", | 190 | "pricing.features.customWebsites": "Add Custom Websites", |
187 | "pricing.features.desktopNotifications": "Desktop Notifications", | 191 | "pricing.features.desktopNotifications": "Desktop Notifications", |
188 | "pricing.features.onPremise": "On-premise & other Hosted Services", | 192 | "pricing.features.onPremise": "On-premise & other Hosted Services", |
@@ -305,8 +309,8 @@ | |||
305 | "settings.app.form.sentry": "Send telemetry data", | 309 | "settings.app.form.sentry": "Send telemetry data", |
306 | "settings.app.form.serviceRibbonWidth": "Sidebar width", | 310 | "settings.app.form.serviceRibbonWidth": "Sidebar width", |
307 | "settings.app.form.showDisabledServices": "Display disabled services tabs", | 311 | "settings.app.form.showDisabledServices": "Display disabled services tabs", |
308 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", | ||
309 | "settings.app.form.showDragArea": "Show draggable area on window", | 312 | "settings.app.form.showDragArea": "Show draggable area on window", |
313 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", | ||
310 | "settings.app.form.startMinimized": "Start minimized", | 314 | "settings.app.form.startMinimized": "Start minimized", |
311 | "settings.app.form.universalDarkMode": "Enable universal Dark Mode", | 315 | "settings.app.form.universalDarkMode": "Enable universal Dark Mode", |
312 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", | 316 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", |
@@ -366,6 +370,8 @@ | |||
366 | "settings.service.form.customUrlUpgradeAccount": "Upgrade your account", | 370 | "settings.service.form.customUrlUpgradeAccount": "Upgrade your account", |
367 | "settings.service.form.customUrlValidationError": "Could not validate custom {name} server.", | 371 | "settings.service.form.customUrlValidationError": "Could not validate custom {name} server.", |
368 | "settings.service.form.deleteButton": "Delete service", | 372 | "settings.service.form.deleteButton": "Delete service", |
373 | "settings.service.form.disableHibernation": "Disable hibernation", | ||
374 | "settings.service.form.disableHibernationInfo": "You currently have hibernation enabled but you can disable hibernation for individual services using this option.", | ||
369 | "settings.service.form.editServiceHeadline": "Edit {name}", | 375 | "settings.service.form.editServiceHeadline": "Edit {name}", |
370 | "settings.service.form.enableAudio": "Enable audio", | 376 | "settings.service.form.enableAudio": "Enable audio", |
371 | "settings.service.form.enableBadge": "Show unread message badges", | 377 | "settings.service.form.enableBadge": "Show unread message badges", |
diff --git a/src/i18n/messages/src/components/services/content/ConnectionBanner.json b/src/i18n/messages/src/components/services/content/ConnectionBanner.json new file mode 100644 index 000000000..1047c28b5 --- /dev/null +++ b/src/i18n/messages/src/components/services/content/ConnectionBanner.json | |||
@@ -0,0 +1,67 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "webControls.goHome", | ||
4 | "defaultMessage": "!!!Home", | ||
5 | "file": "src/components/services/content/ConnectionBanner.js", | ||
6 | "start": { | ||
7 | "line": 13, | ||
8 | "column": 10 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 16, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "webControls.openInBrowser", | ||
17 | "defaultMessage": "!!!Open in Browser", | ||
18 | "file": "src/components/services/content/ConnectionBanner.js", | ||
19 | "start": { | ||
20 | "line": 17, | ||
21 | "column": 17 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 20, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "webControls.back", | ||
30 | "defaultMessage": "!!!Back", | ||
31 | "file": "src/components/services/content/ConnectionBanner.js", | ||
32 | "start": { | ||
33 | "line": 21, | ||
34 | "column": 8 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 24, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "webControls.forward", | ||
43 | "defaultMessage": "!!!Forward", | ||
44 | "file": "src/components/services/content/ConnectionBanner.js", | ||
45 | "start": { | ||
46 | "line": 25, | ||
47 | "column": 11 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 28, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "webControls.reload", | ||
56 | "defaultMessage": "!!!Reload", | ||
57 | "file": "src/components/services/content/ConnectionBanner.js", | ||
58 | "start": { | ||
59 | "line": 29, | ||
60 | "column": 10 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 32, | ||
64 | "column": 3 | ||
65 | } | ||
66 | } | ||
67 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/components/services/content/ConnectionLost.json b/src/i18n/messages/src/components/services/content/ConnectionLost.json new file mode 100644 index 000000000..ee3f7a4ba --- /dev/null +++ b/src/i18n/messages/src/components/services/content/ConnectionLost.json | |||
@@ -0,0 +1,67 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "webControls.goHome", | ||
4 | "defaultMessage": "!!!Home", | ||
5 | "file": "src/components/services/content/ConnectionLost.js", | ||
6 | "start": { | ||
7 | "line": 13, | ||
8 | "column": 10 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 16, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "webControls.openInBrowser", | ||
17 | "defaultMessage": "!!!Open in Browser", | ||
18 | "file": "src/components/services/content/ConnectionLost.js", | ||
19 | "start": { | ||
20 | "line": 17, | ||
21 | "column": 17 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 20, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "webControls.back", | ||
30 | "defaultMessage": "!!!Back", | ||
31 | "file": "src/components/services/content/ConnectionLost.js", | ||
32 | "start": { | ||
33 | "line": 21, | ||
34 | "column": 8 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 24, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "webControls.forward", | ||
43 | "defaultMessage": "!!!Forward", | ||
44 | "file": "src/components/services/content/ConnectionLost.js", | ||
45 | "start": { | ||
46 | "line": 25, | ||
47 | "column": 11 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 28, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "webControls.reload", | ||
56 | "defaultMessage": "!!!Reload", | ||
57 | "file": "src/components/services/content/ConnectionLost.js", | ||
58 | "start": { | ||
59 | "line": 29, | ||
60 | "column": 10 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 32, | ||
64 | "column": 3 | ||
65 | } | ||
66 | } | ||
67 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json b/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json new file mode 100644 index 000000000..fd7019f41 --- /dev/null +++ b/src/i18n/messages/src/components/services/content/ConnectionLostBanner.json | |||
@@ -0,0 +1,41 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "connectionLostBanner.message", | ||
4 | "defaultMessage": "!!!Oh no! Franz lost the connection to {name}.", | ||
5 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
6 | "start": { | ||
7 | "line": 15, | ||
8 | "column": 8 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 18, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "connectionLostBanner.informationLink", | ||
17 | "defaultMessage": "!!!What happened?", | ||
18 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
19 | "start": { | ||
20 | "line": 19, | ||
21 | "column": 19 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 22, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "connectionLostBanner.cta", | ||
30 | "defaultMessage": "!!!Reload Service", | ||
31 | "file": "src/components/services/content/ConnectionLostBanner.js", | ||
32 | "start": { | ||
33 | "line": 23, | ||
34 | "column": 7 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 26, | ||
38 | "column": 3 | ||
39 | } | ||
40 | } | ||
41 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/components/services/content/WebControls.json b/src/i18n/messages/src/components/services/content/WebControls.json new file mode 100644 index 000000000..5af5143d0 --- /dev/null +++ b/src/i18n/messages/src/components/services/content/WebControls.json | |||
@@ -0,0 +1,67 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "webControls.goHome", | ||
4 | "defaultMessage": "!!!Home", | ||
5 | "file": "src/components/services/content/WebControls.js", | ||
6 | "start": { | ||
7 | "line": 13, | ||
8 | "column": 10 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 16, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "webControls.openInBrowser", | ||
17 | "defaultMessage": "!!!Open in Browser", | ||
18 | "file": "src/components/services/content/WebControls.js", | ||
19 | "start": { | ||
20 | "line": 17, | ||
21 | "column": 17 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 20, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "webControls.back", | ||
30 | "defaultMessage": "!!!Back", | ||
31 | "file": "src/components/services/content/WebControls.js", | ||
32 | "start": { | ||
33 | "line": 21, | ||
34 | "column": 8 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 24, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "webControls.forward", | ||
43 | "defaultMessage": "!!!Forward", | ||
44 | "file": "src/components/services/content/WebControls.js", | ||
45 | "start": { | ||
46 | "line": 25, | ||
47 | "column": 11 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 28, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "webControls.reload", | ||
56 | "defaultMessage": "!!!Reload", | ||
57 | "file": "src/components/services/content/WebControls.js", | ||
58 | "start": { | ||
59 | "line": 29, | ||
60 | "column": 10 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 32, | ||
64 | "column": 3 | ||
65 | } | ||
66 | } | ||
67 | ] \ No newline at end of file | ||
diff --git a/src/i18n/messages/src/components/settings/services/EditServiceForm.json b/src/i18n/messages/src/components/settings/services/EditServiceForm.json index df64c8a5f..811c49498 100644 --- a/src/i18n/messages/src/components/settings/services/EditServiceForm.json +++ b/src/i18n/messages/src/components/settings/services/EditServiceForm.json | |||
@@ -234,15 +234,28 @@ | |||
234 | } | 234 | } |
235 | }, | 235 | }, |
236 | { | 236 | { |
237 | "id": "settings.service.form.disableHibernationInfo", | ||
238 | "defaultMessage": "!!!You currently have hibernation enabled but you can disable hibernation for individual services using this option.", | ||
239 | "file": "src/components/settings/services/EditServiceForm.js", | ||
240 | "start": { | ||
241 | "line": 96, | ||
242 | "column": 26 | ||
243 | }, | ||
244 | "end": { | ||
245 | "line": 99, | ||
246 | "column": 3 | ||
247 | } | ||
248 | }, | ||
249 | { | ||
237 | "id": "settings.service.form.headlineNotifications", | 250 | "id": "settings.service.form.headlineNotifications", |
238 | "defaultMessage": "!!!Notifications", | 251 | "defaultMessage": "!!!Notifications", |
239 | "file": "src/components/settings/services/EditServiceForm.js", | 252 | "file": "src/components/settings/services/EditServiceForm.js", |
240 | "start": { | 253 | "start": { |
241 | "line": 96, | 254 | "line": 100, |
242 | "column": 25 | 255 | "column": 25 |
243 | }, | 256 | }, |
244 | "end": { | 257 | "end": { |
245 | "line": 99, | 258 | "line": 103, |
246 | "column": 3 | 259 | "column": 3 |
247 | } | 260 | } |
248 | }, | 261 | }, |
@@ -251,11 +264,11 @@ | |||
251 | "defaultMessage": "!!!Unread message badges", | 264 | "defaultMessage": "!!!Unread message badges", |
252 | "file": "src/components/settings/services/EditServiceForm.js", | 265 | "file": "src/components/settings/services/EditServiceForm.js", |
253 | "start": { | 266 | "start": { |
254 | "line": 100, | 267 | "line": 104, |
255 | "column": 18 | 268 | "column": 18 |
256 | }, | 269 | }, |
257 | "end": { | 270 | "end": { |
258 | "line": 103, | 271 | "line": 107, |
259 | "column": 3 | 272 | "column": 3 |
260 | } | 273 | } |
261 | }, | 274 | }, |
@@ -264,11 +277,11 @@ | |||
264 | "defaultMessage": "!!!General", | 277 | "defaultMessage": "!!!General", |
265 | "file": "src/components/settings/services/EditServiceForm.js", | 278 | "file": "src/components/settings/services/EditServiceForm.js", |
266 | "start": { | 279 | "start": { |
267 | "line": 104, | 280 | "line": 108, |
268 | "column": 19 | 281 | "column": 19 |
269 | }, | 282 | }, |
270 | "end": { | 283 | "end": { |
271 | "line": 107, | 284 | "line": 111, |
272 | "column": 3 | 285 | "column": 3 |
273 | } | 286 | } |
274 | }, | 287 | }, |
@@ -277,11 +290,11 @@ | |||
277 | "defaultMessage": "!!!Delete", | 290 | "defaultMessage": "!!!Delete", |
278 | "file": "src/components/settings/services/EditServiceForm.js", | 291 | "file": "src/components/settings/services/EditServiceForm.js", |
279 | "start": { | 292 | "start": { |
280 | "line": 108, | 293 | "line": 112, |
281 | "column": 14 | 294 | "column": 14 |
282 | }, | 295 | }, |
283 | "end": { | 296 | "end": { |
284 | "line": 111, | 297 | "line": 115, |
285 | "column": 3 | 298 | "column": 3 |
286 | } | 299 | } |
287 | }, | 300 | }, |
@@ -290,11 +303,11 @@ | |||
290 | "defaultMessage": "!!!Drop your image, or click here", | 303 | "defaultMessage": "!!!Drop your image, or click here", |
291 | "file": "src/components/settings/services/EditServiceForm.js", | 304 | "file": "src/components/settings/services/EditServiceForm.js", |
292 | "start": { | 305 | "start": { |
293 | "line": 112, | 306 | "line": 116, |
294 | "column": 14 | 307 | "column": 14 |
295 | }, | 308 | }, |
296 | "end": { | 309 | "end": { |
297 | "line": 115, | 310 | "line": 119, |
298 | "column": 3 | 311 | "column": 3 |
299 | } | 312 | } |
300 | }, | 313 | }, |
@@ -303,11 +316,11 @@ | |||
303 | "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", | 316 | "defaultMessage": "!!!HTTP/HTTPS Proxy Settings", |
304 | "file": "src/components/settings/services/EditServiceForm.js", | 317 | "file": "src/components/settings/services/EditServiceForm.js", |
305 | "start": { | 318 | "start": { |
306 | "line": 116, | 319 | "line": 120, |
307 | "column": 17 | 320 | "column": 17 |
308 | }, | 321 | }, |
309 | "end": { | 322 | "end": { |
310 | "line": 119, | 323 | "line": 123, |
311 | "column": 3 | 324 | "column": 3 |
312 | } | 325 | } |
313 | }, | 326 | }, |
@@ -316,11 +329,11 @@ | |||
316 | "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", | 329 | "defaultMessage": "!!!Please restart Ferdi after changing proxy Settings.", |
317 | "file": "src/components/settings/services/EditServiceForm.js", | 330 | "file": "src/components/settings/services/EditServiceForm.js", |
318 | "start": { | 331 | "start": { |
319 | "line": 120, | 332 | "line": 124, |
320 | "column": 20 | 333 | "column": 20 |
321 | }, | 334 | }, |
322 | "end": { | 335 | "end": { |
323 | "line": 123, | 336 | "line": 127, |
324 | "column": 3 | 337 | "column": 3 |
325 | } | 338 | } |
326 | }, | 339 | }, |
@@ -329,11 +342,11 @@ | |||
329 | "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", | 342 | "defaultMessage": "!!!Proxy settings will not be synchronized with the Ferdi servers.", |
330 | "file": "src/components/settings/services/EditServiceForm.js", | 343 | "file": "src/components/settings/services/EditServiceForm.js", |
331 | "start": { | 344 | "start": { |
332 | "line": 124, | 345 | "line": 128, |
333 | "column": 13 | 346 | "column": 13 |
334 | }, | 347 | }, |
335 | "end": { | 348 | "end": { |
336 | "line": 127, | 349 | "line": 131, |
337 | "column": 3 | 350 | "column": 3 |
338 | } | 351 | } |
339 | } | 352 | } |
diff --git a/src/i18n/messages/src/components/ui/FeatureList.json b/src/i18n/messages/src/components/ui/FeatureList.json index 3201115b3..8d1dc4360 100644 --- a/src/i18n/messages/src/components/ui/FeatureList.json +++ b/src/i18n/messages/src/components/ui/FeatureList.json | |||
@@ -193,5 +193,18 @@ | |||
193 | "line": 68, | 193 | "line": 68, |
194 | "column": 3 | 194 | "column": 3 |
195 | } | 195 | } |
196 | }, | ||
197 | { | ||
198 | "id": "pricing.features.appDelaysEnabled", | ||
199 | "defaultMessage": "!!!Occasional Waiting Screens", | ||
200 | "file": "src/components/ui/FeatureList.js", | ||
201 | "start": { | ||
202 | "line": 69, | ||
203 | "column": 19 | ||
204 | }, | ||
205 | "end": { | ||
206 | "line": 72, | ||
207 | "column": 3 | ||
208 | } | ||
196 | } | 209 | } |
197 | ] \ No newline at end of file | 210 | ] \ No newline at end of file |
diff --git a/src/i18n/messages/src/containers/settings/EditServiceScreen.json b/src/i18n/messages/src/containers/settings/EditServiceScreen.json index 9b46a1e6f..36937ebf8 100644 --- a/src/i18n/messages/src/containers/settings/EditServiceScreen.json +++ b/src/i18n/messages/src/containers/settings/EditServiceScreen.json | |||
@@ -26,8 +26,8 @@ | |||
26 | } | 26 | } |
27 | }, | 27 | }, |
28 | { | 28 | { |
29 | "id": "settings.service.form.enableNotification", | 29 | "id": "settings.service.form.disableHibernation", |
30 | "defaultMessage": "!!!Enable Notifications", | 30 | "defaultMessage": "!!!Disable hibernation", |
31 | "file": "src/containers/settings/EditServiceScreen.js", | 31 | "file": "src/containers/settings/EditServiceScreen.js", |
32 | "start": { | 32 | "start": { |
33 | "line": 36, | 33 | "line": 36, |
@@ -39,15 +39,28 @@ | |||
39 | } | 39 | } |
40 | }, | 40 | }, |
41 | { | 41 | { |
42 | "id": "settings.service.form.enableNotification", | ||
43 | "defaultMessage": "!!!Enable Notifications", | ||
44 | "file": "src/containers/settings/EditServiceScreen.js", | ||
45 | "start": { | ||
46 | "line": 40, | ||
47 | "column": 22 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 43, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
42 | "id": "settings.service.form.enableBadge", | 55 | "id": "settings.service.form.enableBadge", |
43 | "defaultMessage": "!!!Show unread message badges", | 56 | "defaultMessage": "!!!Show unread message badges", |
44 | "file": "src/containers/settings/EditServiceScreen.js", | 57 | "file": "src/containers/settings/EditServiceScreen.js", |
45 | "start": { | 58 | "start": { |
46 | "line": 40, | 59 | "line": 44, |
47 | "column": 15 | 60 | "column": 15 |
48 | }, | 61 | }, |
49 | "end": { | 62 | "end": { |
50 | "line": 43, | 63 | "line": 47, |
51 | "column": 3 | 64 | "column": 3 |
52 | } | 65 | } |
53 | }, | 66 | }, |
@@ -56,11 +69,11 @@ | |||
56 | "defaultMessage": "!!!Enable audio", | 69 | "defaultMessage": "!!!Enable audio", |
57 | "file": "src/containers/settings/EditServiceScreen.js", | 70 | "file": "src/containers/settings/EditServiceScreen.js", |
58 | "start": { | 71 | "start": { |
59 | "line": 44, | 72 | "line": 48, |
60 | "column": 15 | 73 | "column": 15 |
61 | }, | 74 | }, |
62 | "end": { | 75 | "end": { |
63 | "line": 47, | 76 | "line": 51, |
64 | "column": 3 | 77 | "column": 3 |
65 | } | 78 | } |
66 | }, | 79 | }, |
@@ -69,11 +82,11 @@ | |||
69 | "defaultMessage": "!!!Team", | 82 | "defaultMessage": "!!!Team", |
70 | "file": "src/containers/settings/EditServiceScreen.js", | 83 | "file": "src/containers/settings/EditServiceScreen.js", |
71 | "start": { | 84 | "start": { |
72 | "line": 48, | 85 | "line": 52, |
73 | "column": 8 | 86 | "column": 8 |
74 | }, | 87 | }, |
75 | "end": { | 88 | "end": { |
76 | "line": 51, | 89 | "line": 55, |
77 | "column": 3 | 90 | "column": 3 |
78 | } | 91 | } |
79 | }, | 92 | }, |
@@ -82,11 +95,11 @@ | |||
82 | "defaultMessage": "!!!Service URL", | 95 | "defaultMessage": "!!!Service URL", |
83 | "file": "src/containers/settings/EditServiceScreen.js", | 96 | "file": "src/containers/settings/EditServiceScreen.js", |
84 | "start": { | 97 | "start": { |
85 | "line": 52, | 98 | "line": 56, |
86 | "column": 13 | 99 | "column": 13 |
87 | }, | 100 | }, |
88 | "end": { | 101 | "end": { |
89 | "line": 55, | 102 | "line": 59, |
90 | "column": 3 | 103 | "column": 3 |
91 | } | 104 | } |
92 | }, | 105 | }, |
@@ -95,11 +108,11 @@ | |||
95 | "defaultMessage": "!!!Show message badge for all new messages", | 108 | "defaultMessage": "!!!Show message badge for all new messages", |
96 | "file": "src/containers/settings/EditServiceScreen.js", | 109 | "file": "src/containers/settings/EditServiceScreen.js", |
97 | "start": { | 110 | "start": { |
98 | "line": 56, | 111 | "line": 60, |
99 | "column": 20 | 112 | "column": 20 |
100 | }, | 113 | }, |
101 | "end": { | 114 | "end": { |
102 | "line": 59, | 115 | "line": 63, |
103 | "column": 3 | 116 | "column": 3 |
104 | } | 117 | } |
105 | }, | 118 | }, |
@@ -108,11 +121,11 @@ | |||
108 | "defaultMessage": "!!!Custom icon", | 121 | "defaultMessage": "!!!Custom icon", |
109 | "file": "src/containers/settings/EditServiceScreen.js", | 122 | "file": "src/containers/settings/EditServiceScreen.js", |
110 | "start": { | 123 | "start": { |
111 | "line": 60, | 124 | "line": 64, |
112 | "column": 8 | 125 | "column": 8 |
113 | }, | 126 | }, |
114 | "end": { | 127 | "end": { |
115 | "line": 63, | 128 | "line": 67, |
116 | "column": 3 | 129 | "column": 3 |
117 | } | 130 | } |
118 | }, | 131 | }, |
@@ -121,11 +134,11 @@ | |||
121 | "defaultMessage": "!!!Enable Dark Mode", | 134 | "defaultMessage": "!!!Enable Dark Mode", |
122 | "file": "src/containers/settings/EditServiceScreen.js", | 135 | "file": "src/containers/settings/EditServiceScreen.js", |
123 | "start": { | 136 | "start": { |
124 | "line": 64, | 137 | "line": 68, |
125 | "column": 18 | 138 | "column": 18 |
126 | }, | 139 | }, |
127 | "end": { | 140 | "end": { |
128 | "line": 67, | 141 | "line": 71, |
129 | "column": 3 | 142 | "column": 3 |
130 | } | 143 | } |
131 | }, | 144 | }, |
@@ -134,11 +147,11 @@ | |||
134 | "defaultMessage": "!!!Use Proxy", | 147 | "defaultMessage": "!!!Use Proxy", |
135 | "file": "src/containers/settings/EditServiceScreen.js", | 148 | "file": "src/containers/settings/EditServiceScreen.js", |
136 | "start": { | 149 | "start": { |
137 | "line": 68, | 150 | "line": 72, |
138 | "column": 15 | 151 | "column": 15 |
139 | }, | 152 | }, |
140 | "end": { | 153 | "end": { |
141 | "line": 71, | 154 | "line": 75, |
142 | "column": 3 | 155 | "column": 3 |
143 | } | 156 | } |
144 | }, | 157 | }, |
@@ -147,11 +160,11 @@ | |||
147 | "defaultMessage": "!!!Proxy Host/IP", | 160 | "defaultMessage": "!!!Proxy Host/IP", |
148 | "file": "src/containers/settings/EditServiceScreen.js", | 161 | "file": "src/containers/settings/EditServiceScreen.js", |
149 | "start": { | 162 | "start": { |
150 | "line": 72, | 163 | "line": 76, |
151 | "column": 13 | 164 | "column": 13 |
152 | }, | 165 | }, |
153 | "end": { | 166 | "end": { |
154 | "line": 75, | 167 | "line": 79, |
155 | "column": 3 | 168 | "column": 3 |
156 | } | 169 | } |
157 | }, | 170 | }, |
@@ -160,11 +173,11 @@ | |||
160 | "defaultMessage": "!!!Port", | 173 | "defaultMessage": "!!!Port", |
161 | "file": "src/containers/settings/EditServiceScreen.js", | 174 | "file": "src/containers/settings/EditServiceScreen.js", |
162 | "start": { | 175 | "start": { |
163 | "line": 76, | 176 | "line": 80, |
164 | "column": 13 | 177 | "column": 13 |
165 | }, | 178 | }, |
166 | "end": { | 179 | "end": { |
167 | "line": 79, | 180 | "line": 83, |
168 | "column": 3 | 181 | "column": 3 |
169 | } | 182 | } |
170 | }, | 183 | }, |
@@ -173,11 +186,11 @@ | |||
173 | "defaultMessage": "!!!User", | 186 | "defaultMessage": "!!!User", |
174 | "file": "src/containers/settings/EditServiceScreen.js", | 187 | "file": "src/containers/settings/EditServiceScreen.js", |
175 | "start": { | 188 | "start": { |
176 | "line": 80, | 189 | "line": 84, |
177 | "column": 13 | 190 | "column": 13 |
178 | }, | 191 | }, |
179 | "end": { | 192 | "end": { |
180 | "line": 83, | 193 | "line": 87, |
181 | "column": 3 | 194 | "column": 3 |
182 | } | 195 | } |
183 | }, | 196 | }, |
@@ -186,11 +199,11 @@ | |||
186 | "defaultMessage": "!!!Password", | 199 | "defaultMessage": "!!!Password", |
187 | "file": "src/containers/settings/EditServiceScreen.js", | 200 | "file": "src/containers/settings/EditServiceScreen.js", |
188 | "start": { | 201 | "start": { |
189 | "line": 84, | 202 | "line": 88, |
190 | "column": 17 | 203 | "column": 17 |
191 | }, | 204 | }, |
192 | "end": { | 205 | "end": { |
193 | "line": 87, | 206 | "line": 91, |
194 | "column": 3 | 207 | "column": 3 |
195 | } | 208 | } |
196 | } | 209 | } |
diff --git a/src/i18n/messages/src/containers/settings/EditSettingsScreen.json b/src/i18n/messages/src/containers/settings/EditSettingsScreen.json index 5e084c1e1..42199503b 100644 --- a/src/i18n/messages/src/containers/settings/EditSettingsScreen.json +++ b/src/i18n/messages/src/containers/settings/EditSettingsScreen.json | |||
@@ -300,7 +300,7 @@ | |||
300 | }, | 300 | }, |
301 | { | 301 | { |
302 | "id": "settings.app.form.adaptableDarkMode", | 302 | "id": "settings.app.form.adaptableDarkMode", |
303 | "defaultMessage": "!!!Synchronize dark mode with my Mac's dark mode setting", | 303 | "defaultMessage": "!!!Synchronize dark mode with my OS's dark mode setting", |
304 | "file": "src/containers/settings/EditSettingsScreen.js", | 304 | "file": "src/containers/settings/EditSettingsScreen.js", |
305 | "start": { | 305 | "start": { |
306 | "line": 121, | 306 | "line": 121, |
diff --git a/src/i18n/messages/src/features/recipeConnectionLost/components/WebControls.json b/src/i18n/messages/src/features/recipeConnectionLost/components/WebControls.json new file mode 100644 index 000000000..f3bcaf345 --- /dev/null +++ b/src/i18n/messages/src/features/recipeConnectionLost/components/WebControls.json | |||
@@ -0,0 +1,67 @@ | |||
1 | [ | ||
2 | { | ||
3 | "id": "webControls.goHome", | ||
4 | "defaultMessage": "!!!Home", | ||
5 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
6 | "start": { | ||
7 | "line": 13, | ||
8 | "column": 10 | ||
9 | }, | ||
10 | "end": { | ||
11 | "line": 16, | ||
12 | "column": 3 | ||
13 | } | ||
14 | }, | ||
15 | { | ||
16 | "id": "webControls.openInBrowser", | ||
17 | "defaultMessage": "!!!Open in Browser", | ||
18 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
19 | "start": { | ||
20 | "line": 17, | ||
21 | "column": 17 | ||
22 | }, | ||
23 | "end": { | ||
24 | "line": 20, | ||
25 | "column": 3 | ||
26 | } | ||
27 | }, | ||
28 | { | ||
29 | "id": "webControls.back", | ||
30 | "defaultMessage": "!!!Back", | ||
31 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
32 | "start": { | ||
33 | "line": 21, | ||
34 | "column": 8 | ||
35 | }, | ||
36 | "end": { | ||
37 | "line": 24, | ||
38 | "column": 3 | ||
39 | } | ||
40 | }, | ||
41 | { | ||
42 | "id": "webControls.forward", | ||
43 | "defaultMessage": "!!!Forward", | ||
44 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
45 | "start": { | ||
46 | "line": 25, | ||
47 | "column": 11 | ||
48 | }, | ||
49 | "end": { | ||
50 | "line": 28, | ||
51 | "column": 3 | ||
52 | } | ||
53 | }, | ||
54 | { | ||
55 | "id": "webControls.reload", | ||
56 | "defaultMessage": "!!!Reload", | ||
57 | "file": "src/features/recipeConnectionLost/components/WebControls.js", | ||
58 | "start": { | ||
59 | "line": 29, | ||
60 | "column": 10 | ||
61 | }, | ||
62 | "end": { | ||
63 | "line": 32, | ||
64 | "column": 3 | ||
65 | } | ||
66 | } | ||
67 | ] \ No newline at end of file | ||
diff --git a/src/index.js b/src/index.js index 594097288..e5f678759 100644 --- a/src/index.js +++ b/src/index.js | |||
@@ -37,6 +37,7 @@ import Tray from './lib/Tray'; | |||
37 | import Settings from './electron/Settings'; | 37 | import Settings from './electron/Settings'; |
38 | import handleDeepLink from './electron/deepLinking'; | 38 | import handleDeepLink from './electron/deepLinking'; |
39 | import { isPositionValid } from './electron/windowUtils'; | 39 | import { isPositionValid } from './electron/windowUtils'; |
40 | // import askFormacOSPermissions from './electron/macOSPermissions'; | ||
40 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved | 41 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved |
41 | import './electron/exception'; | 42 | import './electron/exception'; |
42 | 43 | ||
@@ -46,10 +47,14 @@ import { | |||
46 | } from './config'; | 47 | } from './config'; |
47 | import { asarPath } from './helpers/asar-helpers'; | 48 | import { asarPath } from './helpers/asar-helpers'; |
48 | import { isValidExternalURL } from './helpers/url-helpers'; | 49 | import { isValidExternalURL } from './helpers/url-helpers'; |
49 | /* eslint-enable import/first */ | 50 | import userAgent from './helpers/userAgent-helpers'; |
50 | 51 | ||
51 | const debug = require('debug')('Ferdi:App'); | 52 | const debug = require('debug')('Ferdi:App'); |
52 | 53 | ||
54 | // Globally set useragent to fix user agent override in service workers | ||
55 | debug('Set userAgent to ', userAgent()); | ||
56 | app.userAgentFallback = userAgent(); | ||
57 | |||
53 | // Keep a global reference of the window object, if you don't, the window will | 58 | // Keep a global reference of the window object, if you don't, the window will |
54 | // be closed automatically when the JavaScript object is garbage collected. | 59 | // be closed automatically when the JavaScript object is garbage collected. |
55 | let mainWindow; | 60 | let mainWindow; |
@@ -182,6 +187,7 @@ const createWindow = () => { | |||
182 | nodeIntegration: true, | 187 | nodeIntegration: true, |
183 | webviewTag: true, | 188 | webviewTag: true, |
184 | preload: path.join(__dirname, 'sentry.js'), | 189 | preload: path.join(__dirname, 'sentry.js'), |
190 | enableRemoteModule: true, | ||
185 | }, | 191 | }, |
186 | }); | 192 | }); |
187 | 193 | ||
@@ -291,6 +297,11 @@ const createWindow = () => { | |||
291 | } | 297 | } |
292 | }); | 298 | }); |
293 | 299 | ||
300 | // Asking for permissions like this currently crashes Ferdi | ||
301 | // if (isMac) { | ||
302 | // askFormacOSPermissions(); | ||
303 | // } | ||
304 | |||
294 | mainWindow.on('show', () => { | 305 | mainWindow.on('show', () => { |
295 | debug('Skip taskbar: true'); | 306 | debug('Skip taskbar: true'); |
296 | mainWindow.setSkipTaskbar(false); | 307 | mainWindow.setSkipTaskbar(false); |
diff --git a/src/internal-server b/src/internal-server | |||
Subproject 38fc9925d88971cee26cc08343da2f0e153c053 | Subproject 95ae59926dbd88d55a5377be997558a9e112ab4 | ||
diff --git a/src/models/Recipe.js b/src/models/Recipe.js index 6655f8310..dcb998a19 100644 --- a/src/models/Recipe.js +++ b/src/models/Recipe.js | |||
@@ -38,6 +38,8 @@ export default class Recipe { | |||
38 | 38 | ||
39 | disablewebsecurity = false; | 39 | disablewebsecurity = false; |
40 | 40 | ||
41 | autoHibernate = false; | ||
42 | |||
41 | constructor(data) { | 43 | constructor(data) { |
42 | if (!data) { | 44 | if (!data) { |
43 | throw Error('Recipe config not valid'); | 45 | throw Error('Recipe config not valid'); |
@@ -78,6 +80,8 @@ export default class Recipe { | |||
78 | 80 | ||
79 | this.disablewebsecurity = data.config.disablewebsecurity || this.disablewebsecurity; | 81 | this.disablewebsecurity = data.config.disablewebsecurity || this.disablewebsecurity; |
80 | 82 | ||
83 | this.autoHibernate = data.config.autoHibernate || this.autoHibernate; | ||
84 | |||
81 | this.message = data.config.message || this.message; | 85 | this.message = data.config.message || this.message; |
82 | } | 86 | } |
83 | 87 | ||
diff --git a/src/models/Service.js b/src/models/Service.js index f073ac9fc..dc8febe0b 100644 --- a/src/models/Service.js +++ b/src/models/Service.js | |||
@@ -2,6 +2,8 @@ import { autorun, computed, observable } from 'mobx'; | |||
2 | import normalizeUrl from 'normalize-url'; | 2 | import normalizeUrl from 'normalize-url'; |
3 | import path from 'path'; | 3 | import path from 'path'; |
4 | 4 | ||
5 | import userAgent from '../helpers/userAgent-helpers'; | ||
6 | |||
5 | const debug = require('debug')('Ferdi:Service'); | 7 | const debug = require('debug')('Ferdi:Service'); |
6 | 8 | ||
7 | export const RESTRICTION_TYPES = { | 9 | export const RESTRICTION_TYPES = { |
@@ -74,6 +76,20 @@ export default class Service { | |||
74 | 76 | ||
75 | @observable restrictionType = null; | 77 | @observable restrictionType = null; |
76 | 78 | ||
79 | @observable isHibernationEnabled = false; | ||
80 | |||
81 | @observable lastUsed = Date.now(); // timestamp | ||
82 | |||
83 | @observable lastPoll = null; | ||
84 | |||
85 | @observable lastPollAnswer = null; | ||
86 | |||
87 | @observable lostRecipeConnection = false; | ||
88 | |||
89 | @observable lostRecipeReloadAttempt = 0; | ||
90 | |||
91 | @observable chromelessUserAgent = false; | ||
92 | |||
77 | constructor(data, recipe) { | 93 | constructor(data, recipe) { |
78 | if (!data) { | 94 | if (!data) { |
79 | console.error('Service config not valid'); | 95 | console.error('Service config not valid'); |
@@ -119,6 +135,8 @@ export default class Service { | |||
119 | 135 | ||
120 | this.spellcheckerLanguage = data.spellcheckerLanguage !== undefined ? data.spellcheckerLanguage : this.spellcheckerLanguage; | 136 | this.spellcheckerLanguage = data.spellcheckerLanguage !== undefined ? data.spellcheckerLanguage : this.spellcheckerLanguage; |
121 | 137 | ||
138 | this.isHibernationEnabled = data.isHibernationEnabled !== undefined ? data.isHibernationEnabled : this.isHibernationEnabled; | ||
139 | |||
122 | this.recipe = recipe; | 140 | this.recipe = recipe; |
123 | 141 | ||
124 | autorun(() => { | 142 | autorun(() => { |
@@ -187,21 +205,34 @@ export default class Service { | |||
187 | } | 205 | } |
188 | 206 | ||
189 | @computed get userAgent() { | 207 | @computed get userAgent() { |
190 | let { userAgent } = window.navigator; | 208 | let ua = userAgent(this.chromelessUserAgent); |
191 | if (typeof this.recipe.overrideUserAgent === 'function') { | 209 | if (typeof this.recipe.overrideUserAgent === 'function') { |
192 | userAgent = this.recipe.overrideUserAgent(); | 210 | ua = this.recipe.overrideUserAgent(); |
193 | } | 211 | } |
194 | 212 | ||
195 | // Remove Ferdi as it can cause incompatabilities with services. | 213 | return ua; |
196 | // This way, Ferdi will look like a normal Chrome instance | ||
197 | userAgent = userAgent.replace(/(Ferdi|Electron)([^\s]+\s)/g, ''); | ||
198 | |||
199 | return userAgent; | ||
200 | } | 214 | } |
201 | 215 | ||
202 | initializeWebViewEvents({ handleIPCMessage, openWindow, stores }) { | 216 | initializeWebViewEvents({ handleIPCMessage, openWindow, stores }) { |
203 | const webContents = this.webview.getWebContents(); | 217 | const webContents = this.webview.getWebContents(); |
204 | 218 | ||
219 | const handleUserAgent = (url, forwardingHack = false) => { | ||
220 | if (url.startsWith('https://accounts.google.com')) { | ||
221 | if (!this.chromelessUserAgent) { | ||
222 | debug('Setting user agent to chromeless for url', url); | ||
223 | this.webview.setUserAgent(userAgent(true)); | ||
224 | if (forwardingHack) { | ||
225 | this.webview.loadURL(url); | ||
226 | } | ||
227 | this.chromelessUserAgent = true; | ||
228 | } | ||
229 | } else if (this.chromelessUserAgent) { | ||
230 | debug('Setting user agent to contain chrome'); | ||
231 | this.webview.setUserAgent(this.userAgent); | ||
232 | this.chromelessUserAgent = false; | ||
233 | } | ||
234 | }; | ||
235 | |||
205 | this.webview.addEventListener('ipc-message', e => handleIPCMessage({ | 236 | this.webview.addEventListener('ipc-message', e => handleIPCMessage({ |
206 | serviceId: this.id, | 237 | serviceId: this.id, |
207 | channel: e.channel, | 238 | channel: e.channel, |
@@ -209,7 +240,6 @@ export default class Service { | |||
209 | })); | 240 | })); |
210 | 241 | ||
211 | this.webview.addEventListener('new-window', (event, url, frameName, options) => { | 242 | this.webview.addEventListener('new-window', (event, url, frameName, options) => { |
212 | console.log('open window', event, url, frameName, options); | ||
213 | openWindow({ | 243 | openWindow({ |
214 | event, | 244 | event, |
215 | url, | 245 | url, |
@@ -218,6 +248,9 @@ export default class Service { | |||
218 | }); | 248 | }); |
219 | }); | 249 | }); |
220 | 250 | ||
251 | |||
252 | this.webview.addEventListener('will-navigate', event => handleUserAgent(event.url, true)); | ||
253 | |||
221 | this.webview.addEventListener('did-start-loading', (event) => { | 254 | this.webview.addEventListener('did-start-loading', (event) => { |
222 | debug('Did start load', this.name, event); | 255 | debug('Did start load', this.name, event); |
223 | 256 | ||
@@ -235,7 +268,10 @@ export default class Service { | |||
235 | }; | 268 | }; |
236 | 269 | ||
237 | this.webview.addEventListener('did-frame-finish-load', didLoad.bind(this)); | 270 | this.webview.addEventListener('did-frame-finish-load', didLoad.bind(this)); |
238 | this.webview.addEventListener('did-navigate', didLoad.bind(this)); | 271 | this.webview.addEventListener('did-navigate', (event) => { |
272 | handleUserAgent(event.url); | ||
273 | didLoad(); | ||
274 | }); | ||
239 | 275 | ||
240 | this.webview.addEventListener('did-fail-load', (event) => { | 276 | this.webview.addEventListener('did-fail-load', (event) => { |
241 | debug('Service failed to load', this.name, event); | 277 | debug('Service failed to load', this.name, event); |
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index e2e3760a8..da6055e5f 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -218,13 +218,16 @@ export default class AppStore extends Store { | |||
218 | // macOS catalina notifications hack | 218 | // macOS catalina notifications hack |
219 | // notifications got stuck after upgrade but forcing a notification | 219 | // notifications got stuck after upgrade but forcing a notification |
220 | // via `new Notification` triggered the permission request | 220 | // via `new Notification` triggered the permission request |
221 | if (isMac && !localStorage.getItem(CATALINA_NOTIFICATION_HACK_KEY)) { | 221 | if (isMac) { |
222 | // eslint-disable-next-line no-new | 222 | if (!localStorage.getItem(CATALINA_NOTIFICATION_HACK_KEY)) { |
223 | new window.Notification('Welcome to Franz 5', { | 223 | debug('Triggering macOS Catalina notification permission trigger'); |
224 | body: 'Have a wonderful day & happy messaging.', | 224 | // eslint-disable-next-line no-new |
225 | }); | 225 | new window.Notification('Welcome to Franz 5', { |
226 | body: 'Have a wonderful day & happy messaging.', | ||
227 | }); | ||
226 | 228 | ||
227 | localStorage.setItem(CATALINA_NOTIFICATION_HACK_KEY, true); | 229 | localStorage.setItem(CATALINA_NOTIFICATION_HACK_KEY, true); |
230 | } | ||
228 | } | 231 | } |
229 | } | 232 | } |
230 | 233 | ||
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index 19e6f8299..80c7d7e81 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -5,7 +5,7 @@ import { | |||
5 | computed, | 5 | computed, |
6 | observable, | 6 | observable, |
7 | } from 'mobx'; | 7 | } from 'mobx'; |
8 | import { remove } from 'lodash'; | 8 | import { debounce, remove } from 'lodash'; |
9 | import ms from 'ms'; | 9 | import ms from 'ms'; |
10 | import fs from 'fs-extra'; | 10 | import fs from 'fs-extra'; |
11 | import path from 'path'; | 11 | import path from 'path'; |
@@ -127,6 +127,60 @@ export default class ServicesStore extends Store { | |||
127 | ); | 127 | ); |
128 | } | 128 | } |
129 | 129 | ||
130 | initialize() { | ||
131 | super.initialize(); | ||
132 | |||
133 | // Check services to become hibernated | ||
134 | this.serviceMaintenanceTick(); | ||
135 | } | ||
136 | |||
137 | teardown() { | ||
138 | super.teardown(); | ||
139 | |||
140 | // Stop checking services for hibernation | ||
141 | this.serviceMaintenanceTick.cancel(); | ||
142 | } | ||
143 | |||
144 | /** | ||
145 | * Сheck for services to become hibernated. | ||
146 | */ | ||
147 | serviceMaintenanceTick = debounce(() => { | ||
148 | this._serviceMaintenance(); | ||
149 | this.serviceMaintenanceTick(); | ||
150 | debug('Service maintenance tick'); | ||
151 | }, ms('10s')); | ||
152 | |||
153 | /** | ||
154 | * Run various maintenance tasks on services | ||
155 | */ | ||
156 | _serviceMaintenance() { | ||
157 | this.all.forEach((service) => { | ||
158 | // Defines which services should be hibernated. | ||
159 | if (!service.isActive && (Date.now() - service.lastUsed > ms('5m'))) { | ||
160 | // If service is stale for 5 min, hibernate it. | ||
161 | this._hibernate({ serviceId: service.id }); | ||
162 | } | ||
163 | |||
164 | if (service.lastPoll && (service.lastPoll) - service.lastPollAnswer > ms('30s')) { | ||
165 | // If service did not reply for more than 30s try to reload. | ||
166 | if (!service.isActive) { | ||
167 | if (this.stores.app.isOnline && service.lostRecipeReloadAttempt < 3) { | ||
168 | service.webview.reload(); | ||
169 | service.lostRecipeReloadAttempt += 1; | ||
170 | |||
171 | service.lostRecipeConnection = false; | ||
172 | } | ||
173 | } else { | ||
174 | service.lostRecipeConnection = true; | ||
175 | } | ||
176 | } else { | ||
177 | service.lostRecipeConnection = false; | ||
178 | service.lostRecipeReloadAttempt = 0; | ||
179 | } | ||
180 | }); | ||
181 | } | ||
182 | |||
183 | // Computed props | ||
130 | @computed get all() { | 184 | @computed get all() { |
131 | if (this.stores.user.isLoggedIn) { | 185 | if (this.stores.user.isLoggedIn) { |
132 | const services = this.allServicesRequest.execute().result; | 186 | const services = this.allServicesRequest.execute().result; |
@@ -379,6 +433,7 @@ export default class ServicesStore extends Store { | |||
379 | this.all[index].isActive = false; | 433 | this.all[index].isActive = false; |
380 | }); | 434 | }); |
381 | service.isActive = true; | 435 | service.isActive = true; |
436 | service.lastUsed = Date.now(); | ||
382 | 437 | ||
383 | // Update list of last used services | 438 | // Update list of last used services |
384 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); | 439 | this.lastUsedServices = this.lastUsedServices.filter(id => id !== serviceId); |
@@ -475,10 +530,16 @@ export default class ServicesStore extends Store { | |||
475 | const service = this.one(serviceId); | 530 | const service = this.one(serviceId); |
476 | 531 | ||
477 | if (channel === 'hello') { | 532 | if (channel === 'hello') { |
533 | debug('Received hello event from', serviceId); | ||
534 | |||
478 | this._initRecipePolling(service.id); | 535 | this._initRecipePolling(service.id); |
479 | this._initializeServiceRecipeInWebview(serviceId); | 536 | this._initializeServiceRecipeInWebview(serviceId); |
480 | this._shareSettingsWithServiceProcess(); | 537 | this._shareSettingsWithServiceProcess(); |
538 | } else if (channel === 'alive') { | ||
539 | service.lastPollAnswer = Date.now(); | ||
481 | } else if (channel === 'messages') { | 540 | } else if (channel === 'messages') { |
541 | debug(`Received unread message info from '${serviceId}'`, args[0]); | ||
542 | |||
482 | this.actions.service.setUnreadMessageCount({ | 543 | this.actions.service.setUnreadMessageCount({ |
483 | serviceId, | 544 | serviceId, |
484 | count: { | 545 | count: { |
@@ -600,6 +661,7 @@ export default class ServicesStore extends Store { | |||
600 | if (!service.isEnabled) return; | 661 | if (!service.isEnabled) return; |
601 | 662 | ||
602 | service.resetMessageCount(); | 663 | service.resetMessageCount(); |
664 | service.lostRecipeConnection = false; | ||
603 | 665 | ||
604 | // service.webview.loadURL(service.url); | 666 | // service.webview.loadURL(service.url); |
605 | service.webview.reload(); | 667 | service.webview.reload(); |
@@ -777,7 +839,7 @@ export default class ServicesStore extends Store { | |||
777 | const isMuted = isAppMuted || service.isMuted; | 839 | const isMuted = isAppMuted || service.isMuted; |
778 | 840 | ||
779 | if (isAttached) { | 841 | if (isAttached) { |
780 | service.webview.setAudioMuted(isMuted); | 842 | service.webview.audioMuted = isMuted; |
781 | } | 843 | } |
782 | }); | 844 | }); |
783 | } | 845 | } |
@@ -863,6 +925,7 @@ export default class ServicesStore extends Store { | |||
863 | service.webview.send('poll'); | 925 | service.webview.send('poll'); |
864 | 926 | ||
865 | service.timer = setTimeout(loop, delay); | 927 | service.timer = setTimeout(loop, delay); |
928 | service.lastPoll = Date.now(); | ||
866 | }; | 929 | }; |
867 | 930 | ||
868 | loop(); | 931 | loop(); |
diff --git a/src/webview/lib/RecipeWebview.js b/src/webview/lib/RecipeWebview.js index 4fac21c55..add5fffa6 100644 --- a/src/webview/lib/RecipeWebview.js +++ b/src/webview/lib/RecipeWebview.js | |||
@@ -14,6 +14,10 @@ class RecipeWebview { | |||
14 | this.loopFunc(); | 14 | this.loopFunc(); |
15 | 15 | ||
16 | debug('Poll event'); | 16 | debug('Poll event'); |
17 | |||
18 | // This event is for checking if the service recipe is still actively | ||
19 | // communicating with the client | ||
20 | ipcRenderer.sendToHost('alive'); | ||
17 | }); | 21 | }); |
18 | } | 22 | } |
19 | 23 | ||