diff options
author | Vijay A <vraravam@users.noreply.github.com> | 2021-10-18 07:33:47 +0530 |
---|---|---|
committer | Vijay A <vraravam@users.noreply.github.com> | 2021-10-18 07:33:47 +0530 |
commit | 3e28975d32315444c4c535fda7ba2aa08a3a0bc2 (patch) | |
tree | ef7f7d9083b1d437b2cf68005cd6b831f526729c /src | |
parent | Bumped up version to: 5.6.3-beta.1 [skip ci] (diff) | |
parent | 5.6.3-nightly.37 [skip ci] (diff) | |
download | ferdium-app-3e28975d32315444c4c535fda7ba2aa08a3a0bc2.tar.gz ferdium-app-3e28975d32315444c4c535fda7ba2aa08a3a0bc2.tar.zst ferdium-app-3e28975d32315444c4c535fda7ba2aa08a3a0bc2.zip |
Merge branch 'nightly' into release
Diffstat (limited to 'src')
260 files changed, 5381 insertions, 2252 deletions
diff --git a/src/I18n.js b/src/I18n.js index 6fb4cdc61..2a50050ee 100644 --- a/src/I18n.js +++ b/src/I18n.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { IntlProvider } from 'react-intl'; | 4 | import { IntlProvider } from 'react-intl'; |
diff --git a/src/actions/index.ts b/src/actions/index.ts index aecdac675..983afa64b 100644 --- a/src/actions/index.ts +++ b/src/actions/index.ts | |||
@@ -7,7 +7,6 @@ import recipePreview from './recipePreview'; | |||
7 | import ui from './ui'; | 7 | import ui from './ui'; |
8 | import app from './app'; | 8 | import app from './app'; |
9 | import user from './user'; | 9 | import user from './user'; |
10 | import news from './news'; | ||
11 | import settings from './settings'; | 10 | import settings from './settings'; |
12 | import requests from './requests'; | 11 | import requests from './requests'; |
13 | import workspaces from '../features/workspaces/actions'; | 12 | import workspaces from '../features/workspaces/actions'; |
@@ -20,7 +19,6 @@ const actions = { | |||
20 | ui, | 19 | ui, |
21 | app, | 20 | app, |
22 | user, | 21 | user, |
23 | news, | ||
24 | settings, | 22 | settings, |
25 | requests, | 23 | requests, |
26 | }; | 24 | }; |
diff --git a/src/actions/news.ts b/src/actions/news.ts deleted file mode 100644 index db106e84f..000000000 --- a/src/actions/news.ts +++ /dev/null | |||
@@ -1,7 +0,0 @@ | |||
1 | import PropTypes from 'prop-types'; | ||
2 | |||
3 | export default { | ||
4 | hide: { | ||
5 | newsId: PropTypes.string.isRequired, | ||
6 | }, | ||
7 | }; | ||
diff --git a/src/actions/user.ts b/src/actions/user.ts index 20d27ee53..15a9216bd 100644 --- a/src/actions/user.ts +++ b/src/actions/user.ts | |||
@@ -25,8 +25,10 @@ export default { | |||
25 | userData: PropTypes.object.isRequired, | 25 | userData: PropTypes.object.isRequired, |
26 | }, | 26 | }, |
27 | resetStatus: {}, | 27 | resetStatus: {}, |
28 | importLegacyServices: PropTypes.arrayOf(PropTypes.shape({ | 28 | importLegacyServices: PropTypes.arrayOf( |
29 | recipe: PropTypes.string.isRequired, | 29 | PropTypes.shape({ |
30 | })).isRequired, | 30 | recipe: PropTypes.string.isRequired, |
31 | }), | ||
32 | ).isRequired, | ||
31 | delete: {}, | 33 | delete: {}, |
32 | }; | 34 | }; |
diff --git a/src/api/NewsApi.ts b/src/api/NewsApi.ts deleted file mode 100644 index 31d3d903b..000000000 --- a/src/api/NewsApi.ts +++ /dev/null | |||
@@ -1,18 +0,0 @@ | |||
1 | export default class NewsApi { | ||
2 | server: any; | ||
3 | |||
4 | local: any; | ||
5 | |||
6 | constructor(server: any, local: any) { | ||
7 | this.server = server; | ||
8 | this.local = local; | ||
9 | } | ||
10 | |||
11 | latest() { | ||
12 | return this.server.getLatestNews(); | ||
13 | } | ||
14 | |||
15 | hide(id: any) { | ||
16 | return this.server.hideNews(id); | ||
17 | } | ||
18 | } | ||
diff --git a/src/api/index.ts b/src/api/index.ts index 73f613da1..59fad194a 100644 --- a/src/api/index.ts +++ b/src/api/index.ts | |||
@@ -4,7 +4,6 @@ import RecipePreviewsApi from './RecipePreviewsApi'; | |||
4 | import RecipesApi from './RecipesApi'; | 4 | import RecipesApi from './RecipesApi'; |
5 | import UserApi from './UserApi'; | 5 | import UserApi from './UserApi'; |
6 | import LocalApi from './LocalApi'; | 6 | import LocalApi from './LocalApi'; |
7 | import NewsApi from './NewsApi'; | ||
8 | import FeaturesApi from './FeaturesApi'; | 7 | import FeaturesApi from './FeaturesApi'; |
9 | 8 | ||
10 | export default (server: any, local: any) => ({ | 9 | export default (server: any, local: any) => ({ |
@@ -15,5 +14,4 @@ export default (server: any, local: any) => ({ | |||
15 | features: new FeaturesApi(server), | 14 | features: new FeaturesApi(server), |
16 | user: new UserApi(server, local), | 15 | user: new UserApi(server, local), |
17 | local: new LocalApi(server, local), | 16 | local: new LocalApi(server, local), |
18 | news: new NewsApi(server, local), | ||
19 | }); | 17 | }); |
diff --git a/src/api/server/LocalApi.ts b/src/api/server/LocalApi.ts index 19eacf9ff..1a46aaefe 100644 --- a/src/api/server/LocalApi.ts +++ b/src/api/server/LocalApi.ts | |||
@@ -1,5 +1,6 @@ | |||
1 | import { ExecException } from 'child_process'; | ||
1 | import { ipcRenderer } from 'electron'; | 2 | import { ipcRenderer } from 'electron'; |
2 | import du from 'du'; | 3 | import fastFolderSize from 'fast-folder-size'; |
3 | 4 | ||
4 | import { getServicePartitionsDirectory } from '../../helpers/service-helpers'; | 5 | import { getServicePartitionsDirectory } from '../../helpers/service-helpers'; |
5 | 6 | ||
@@ -29,13 +30,19 @@ export default class LocalApi { | |||
29 | // Services | 30 | // Services |
30 | async getAppCacheSize() { | 31 | async getAppCacheSize() { |
31 | const partitionsDir = getServicePartitionsDirectory(); | 32 | const partitionsDir = getServicePartitionsDirectory(); |
33 | |||
32 | return new Promise((resolve, reject) => { | 34 | return new Promise((resolve, reject) => { |
33 | du(partitionsDir, {}, (err: Error | null, size?: number | undefined) => { | 35 | fastFolderSize( |
34 | if (err) reject(err); | 36 | partitionsDir, |
37 | (err: ExecException | null, bytes: number | undefined) => { | ||
38 | if (err) { | ||
39 | reject(err); | ||
40 | } | ||
35 | 41 | ||
36 | debug('LocalApi::getAppCacheSize resolves', size); | 42 | debug('LocalApi::getAppCacheSize resolves', bytes); |
37 | resolve(size); | 43 | resolve(bytes); |
38 | }); | 44 | }, |
45 | ); | ||
39 | }); | 46 | }); |
40 | } | 47 | } |
41 | 48 | ||
diff --git a/src/api/server/ServerApi.js b/src/api/server/ServerApi.js index e321f9372..6bbf572fa 100644 --- a/src/api/server/ServerApi.js +++ b/src/api/server/ServerApi.js | |||
@@ -16,14 +16,12 @@ import fetch from 'electron-fetch'; | |||
16 | import ServiceModel from '../../models/Service'; | 16 | import ServiceModel from '../../models/Service'; |
17 | import RecipePreviewModel from '../../models/RecipePreview'; | 17 | import RecipePreviewModel from '../../models/RecipePreview'; |
18 | import RecipeModel from '../../models/Recipe'; | 18 | import RecipeModel from '../../models/Recipe'; |
19 | import NewsModel from '../../models/News'; | ||
20 | import UserModel from '../../models/User'; | 19 | import UserModel from '../../models/User'; |
21 | 20 | ||
22 | import { sleep } from '../../helpers/async-helpers'; | 21 | import { sleep } from '../../helpers/async-helpers'; |
23 | 22 | ||
24 | import { SERVER_NOT_LOADED } from '../../config'; | 23 | import { SERVER_NOT_LOADED } from '../../config'; |
25 | import { osArch, osPlatform } from '../../environment'; | 24 | import { userDataRecipesPath, userDataPath } from '../../environment-remote'; |
26 | import { userDataRecipesPath, userDataPath, ferdiVersion } from '../../environment-remote'; | ||
27 | import { asarRecipesPath } from '../../helpers/asar-helpers'; | 25 | import { asarRecipesPath } from '../../helpers/asar-helpers'; |
28 | import apiBase from '../apiBase'; | 26 | import apiBase from '../apiBase'; |
29 | import { prepareAuthRequest, sendAuthRequest } from '../utils/auth'; | 27 | import { prepareAuthRequest, sendAuthRequest } from '../utils/auth'; |
@@ -442,25 +440,6 @@ export default class ServerApi { | |||
442 | } | 440 | } |
443 | } | 441 | } |
444 | 442 | ||
445 | // News | ||
446 | async getLatestNews() { | ||
447 | const url = `${apiBase( | ||
448 | true, | ||
449 | )}/news?platform=${osPlatform}&arch=${osArch}&version=${ferdiVersion}`; | ||
450 | const request = await sendAuthRequest(url); | ||
451 | if (!request.ok) throw request; | ||
452 | const data = await request.json(); | ||
453 | const news = this._mapNewsModels(data); | ||
454 | debug('ServerApi::getLatestNews resolves', news); | ||
455 | return news; | ||
456 | } | ||
457 | |||
458 | async hideNews(id) { | ||
459 | const request = await sendAuthRequest(`${apiBase()}/news/${id}/read`); | ||
460 | if (!request.ok) throw request; | ||
461 | debug('ServerApi::hideNews resolves', id); | ||
462 | } | ||
463 | |||
464 | // Health Check | 443 | // Health Check |
465 | async healthCheck() { | 444 | async healthCheck() { |
466 | if (apiBase() === SERVER_NOT_LOADED) { | 445 | if (apiBase() === SERVER_NOT_LOADED) { |
@@ -587,19 +566,6 @@ export default class ServerApi { | |||
587 | .filter(recipe => recipe !== null); | 566 | .filter(recipe => recipe !== null); |
588 | } | 567 | } |
589 | 568 | ||
590 | _mapNewsModels(news) { | ||
591 | return news | ||
592 | .map(newsItem => { | ||
593 | try { | ||
594 | return new NewsModel(newsItem); | ||
595 | } catch (error) { | ||
596 | console.error(error); | ||
597 | return null; | ||
598 | } | ||
599 | }) | ||
600 | .filter(newsItem => newsItem !== null); | ||
601 | } | ||
602 | |||
603 | _getDevRecipes() { | 569 | _getDevRecipes() { |
604 | const recipesDirectory = getDevRecipeDirectory(); | 570 | const recipesDirectory = getDevRecipeDirectory(); |
605 | try { | 571 | try { |
diff --git a/src/app.js b/src/app.js index 8a1f99320..aea57a673 100644 --- a/src/app.js +++ b/src/app.js | |||
@@ -1,6 +1,5 @@ | |||
1 | import { webFrame } from 'electron'; | 1 | import { webFrame } from 'electron'; |
2 | 2 | ||
3 | import React from 'react'; | ||
4 | import { render } from 'react-dom'; | 3 | import { render } from 'react-dom'; |
5 | import { Provider } from 'mobx-react'; | 4 | import { Provider } from 'mobx-react'; |
6 | import { syncHistoryWithStore, RouterStore } from 'mobx-react-router'; | 5 | import { syncHistoryWithStore, RouterStore } from 'mobx-react-router'; |
diff --git a/src/components/AppUpdateInfoBar.js b/src/components/AppUpdateInfoBar.js deleted file mode 100644 index 47b730bde..000000000 --- a/src/components/AppUpdateInfoBar.js +++ /dev/null | |||
@@ -1,60 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { defineMessages, injectIntl } from 'react-intl'; | ||
4 | |||
5 | import InfoBar from './ui/InfoBar'; | ||
6 | import { GITHUB_FERDI_URL } from '../config'; | ||
7 | import { openExternalUrl } from '../helpers/url-helpers'; | ||
8 | |||
9 | const messages = defineMessages({ | ||
10 | updateAvailable: { | ||
11 | id: 'infobar.updateAvailable', | ||
12 | defaultMessage: 'A new update for Ferdi is available.', | ||
13 | }, | ||
14 | changelog: { | ||
15 | id: 'infobar.buttonChangelog', | ||
16 | defaultMessage: 'What is new?', | ||
17 | }, | ||
18 | buttonInstallUpdate: { | ||
19 | id: 'infobar.buttonInstallUpdate', | ||
20 | defaultMessage: 'Restart & install update', | ||
21 | }, | ||
22 | }); | ||
23 | |||
24 | class AppUpdateInfoBar extends Component { | ||
25 | static propTypes = { | ||
26 | onInstallUpdate: PropTypes.func.isRequired, | ||
27 | onHide: PropTypes.func.isRequired, | ||
28 | }; | ||
29 | |||
30 | render() { | ||
31 | const { intl } = this.props; | ||
32 | const { onInstallUpdate, onHide } = this.props; | ||
33 | |||
34 | return ( | ||
35 | <InfoBar | ||
36 | type="primary" | ||
37 | ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)} | ||
38 | onClick={onInstallUpdate} | ||
39 | onHide={onHide} | ||
40 | > | ||
41 | <span className="mdi mdi-information" /> | ||
42 | {intl.formatMessage(messages.updateAvailable)}{' '} | ||
43 | <button | ||
44 | className="info-bar__inline-button" | ||
45 | type="button" | ||
46 | onClick={() => | ||
47 | openExternalUrl( | ||
48 | `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`, | ||
49 | true, | ||
50 | ) | ||
51 | } | ||
52 | > | ||
53 | <u>{intl.formatMessage(messages.changelog)}</u> | ||
54 | </button> | ||
55 | </InfoBar> | ||
56 | ); | ||
57 | } | ||
58 | } | ||
59 | |||
60 | export default injectIntl(AppUpdateInfoBar); | ||
diff --git a/src/components/AppUpdateInfoBar.tsx b/src/components/AppUpdateInfoBar.tsx new file mode 100644 index 000000000..1dd3723cc --- /dev/null +++ b/src/components/AppUpdateInfoBar.tsx | |||
@@ -0,0 +1,55 @@ | |||
1 | import { defineMessages, useIntl } from 'react-intl'; | ||
2 | |||
3 | import InfoBar from './ui/InfoBar'; | ||
4 | import { GITHUB_FERDI_URL } from '../config'; | ||
5 | import { openExternalUrl } from '../helpers/url-helpers'; | ||
6 | |||
7 | const messages = defineMessages({ | ||
8 | updateAvailable: { | ||
9 | id: 'infobar.updateAvailable', | ||
10 | defaultMessage: 'A new update for Ferdi is available.', | ||
11 | }, | ||
12 | changelog: { | ||
13 | id: 'infobar.buttonChangelog', | ||
14 | defaultMessage: 'What is new?', | ||
15 | }, | ||
16 | buttonInstallUpdate: { | ||
17 | id: 'infobar.buttonInstallUpdate', | ||
18 | defaultMessage: 'Restart & install update', | ||
19 | }, | ||
20 | }); | ||
21 | |||
22 | type Props = { | ||
23 | onInstallUpdate: () => void; | ||
24 | onHide: () => void; | ||
25 | }; | ||
26 | |||
27 | const AppUpdateInfoBar = ({ onInstallUpdate, onHide }: Props) => { | ||
28 | const intl = useIntl(); | ||
29 | |||
30 | return ( | ||
31 | <InfoBar | ||
32 | type="primary" | ||
33 | ctaLabel={intl.formatMessage(messages.buttonInstallUpdate)} | ||
34 | onClick={onInstallUpdate} | ||
35 | onHide={onHide} | ||
36 | > | ||
37 | <span className="mdi mdi-information" /> | ||
38 | {intl.formatMessage(messages.updateAvailable)}{' '} | ||
39 | <button | ||
40 | className="info-bar__inline-button" | ||
41 | type="button" | ||
42 | onClick={() => | ||
43 | openExternalUrl( | ||
44 | `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`, | ||
45 | true, | ||
46 | ) | ||
47 | } | ||
48 | > | ||
49 | <u>{intl.formatMessage(messages.changelog)}</u> | ||
50 | </button> | ||
51 | </InfoBar> | ||
52 | ); | ||
53 | }; | ||
54 | |||
55 | export default AppUpdateInfoBar; | ||
diff --git a/src/components/auth/AuthLayout.js b/src/components/auth/AuthLayout.js index 17ac221a2..00eded728 100644 --- a/src/components/auth/AuthLayout.js +++ b/src/components/auth/AuthLayout.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { cloneElement, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { TitleBar } from 'electron-react-titlebar/renderer'; | 4 | import { TitleBar } from 'electron-react-titlebar/renderer'; |
@@ -87,7 +87,7 @@ class AuthLayout extends Component { | |||
87 | )} | 87 | )} |
88 | <div className="auth__layout"> | 88 | <div className="auth__layout"> |
89 | {/* Inject globalError into children */} | 89 | {/* Inject globalError into children */} |
90 | {React.cloneElement(children, { | 90 | {cloneElement(children, { |
91 | error, | 91 | error, |
92 | })} | 92 | })} |
93 | </div> | 93 | </div> |
diff --git a/src/components/auth/ChangeServer.js b/src/components/auth/ChangeServer.js index b98fb50f7..9aeebc5c8 100644 --- a/src/components/auth/ChangeServer.js +++ b/src/components/auth/ChangeServer.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Import.js b/src/components/auth/Import.js index 44cb7e791..fe2fe9872 100644 --- a/src/components/auth/Import.js +++ b/src/components/auth/Import.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.js index df8980314..dd71c2450 100644 --- a/src/components/auth/Invite.js +++ b/src/components/auth/Invite.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import { Component, Fragment } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Locked.js b/src/components/auth/Locked.js index a507ba140..5b36b9fc2 100644 --- a/src/components/auth/Locked.js +++ b/src/components/auth/Locked.js | |||
@@ -1,5 +1,5 @@ | |||
1 | import { systemPreferences } from '@electron/remote'; | 1 | import { systemPreferences } from '@electron/remote'; |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer } from 'mobx-react'; | 4 | import { observer } from 'mobx-react'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Login.js b/src/components/auth/Login.js index 2f9986858..9f3f636e3 100644 --- a/src/components/auth/Login.js +++ b/src/components/auth/Login.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ | 1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Password.js b/src/components/auth/Password.js index 3e678f638..d5bc7fa80 100644 --- a/src/components/auth/Password.js +++ b/src/components/auth/Password.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/SetupAssistant.js b/src/components/auth/SetupAssistant.js index 299c40c63..1665bf837 100644 --- a/src/components/auth/SetupAssistant.js +++ b/src/components/auth/SetupAssistant.js | |||
@@ -1,12 +1,13 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
6 | import classnames from 'classnames'; | 6 | import classnames from 'classnames'; |
7 | 7 | ||
8 | import { Input, Button } from '@meetfranz/forms'; | 8 | import { Input } from '../ui/input/index'; |
9 | import { Badge } from '@meetfranz/ui'; | 9 | import { Button } from '../ui/button/index'; |
10 | import { Badge } from '../ui/badge'; | ||
10 | import Modal from '../ui/Modal'; | 11 | import Modal from '../ui/Modal'; |
11 | import Infobox from '../ui/Infobox'; | 12 | import Infobox from '../ui/Infobox'; |
12 | import Appear from '../ui/effects/Appear'; | 13 | import Appear from '../ui/effects/Appear'; |
diff --git a/src/components/auth/Signup.js b/src/components/auth/Signup.js index 816a49669..00625a3ac 100644 --- a/src/components/auth/Signup.js +++ b/src/components/auth/Signup.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ | 1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/auth/Welcome.js b/src/components/auth/Welcome.js index 2d2e2ab28..809ec67a7 100644 --- a/src/components/auth/Welcome.js +++ b/src/components/auth/Welcome.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ | 1 | /* eslint jsx-a11y/anchor-is-valid: 0 */ |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; | 4 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/layout/AppLayout.js b/src/components/layout/AppLayout.js index 0a65dcffa..4bacc547b 100644 --- a/src/components/layout/AppLayout.js +++ b/src/components/layout/AppLayout.js | |||
@@ -1,6 +1,6 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import { TitleBar } from 'electron-react-titlebar/renderer'; | 5 | import { TitleBar } from 'electron-react-titlebar/renderer'; |
6 | import injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
@@ -20,10 +20,6 @@ import { workspaceStore } from '../../features/workspaces'; | |||
20 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; | 20 | import AppUpdateInfoBar from '../AppUpdateInfoBar'; |
21 | import Todos from '../../features/todos/containers/TodosScreen'; | 21 | import Todos from '../../features/todos/containers/TodosScreen'; |
22 | 22 | ||
23 | function createMarkup(HTMLString) { | ||
24 | return { __html: HTMLString }; | ||
25 | } | ||
26 | |||
27 | const messages = defineMessages({ | 23 | const messages = defineMessages({ |
28 | servicesUpdated: { | 24 | servicesUpdated: { |
29 | id: 'infobar.servicesUpdated', | 25 | id: 'infobar.servicesUpdated', |
@@ -73,11 +69,9 @@ class AppLayout extends Component { | |||
73 | workspacesDrawer: PropTypes.element.isRequired, | 69 | workspacesDrawer: PropTypes.element.isRequired, |
74 | services: PropTypes.element.isRequired, | 70 | services: PropTypes.element.isRequired, |
75 | children: PropTypes.element, | 71 | children: PropTypes.element, |
76 | news: MobxPropTypes.arrayOrObservableArray.isRequired, | ||
77 | showServicesUpdatedInfoBar: PropTypes.bool.isRequired, | 72 | showServicesUpdatedInfoBar: PropTypes.bool.isRequired, |
78 | appUpdateIsDownloaded: PropTypes.bool.isRequired, | 73 | appUpdateIsDownloaded: PropTypes.bool.isRequired, |
79 | authRequestFailed: PropTypes.bool.isRequired, | 74 | authRequestFailed: PropTypes.bool.isRequired, |
80 | removeNewsItem: PropTypes.func.isRequired, | ||
81 | reloadServicesAfterUpdate: PropTypes.func.isRequired, | 75 | reloadServicesAfterUpdate: PropTypes.func.isRequired, |
82 | installAppUpdate: PropTypes.func.isRequired, | 76 | installAppUpdate: PropTypes.func.isRequired, |
83 | showRequiredRequestsError: PropTypes.bool.isRequired, | 77 | showRequiredRequestsError: PropTypes.bool.isRequired, |
@@ -103,11 +97,9 @@ class AppLayout extends Component { | |||
103 | sidebar, | 97 | sidebar, |
104 | services, | 98 | services, |
105 | children, | 99 | children, |
106 | news, | ||
107 | showServicesUpdatedInfoBar, | 100 | showServicesUpdatedInfoBar, |
108 | appUpdateIsDownloaded, | 101 | appUpdateIsDownloaded, |
109 | authRequestFailed, | 102 | authRequestFailed, |
110 | removeNewsItem, | ||
111 | reloadServicesAfterUpdate, | 103 | reloadServicesAfterUpdate, |
112 | installAppUpdate, | 104 | installAppUpdate, |
113 | showRequiredRequestsError, | 105 | showRequiredRequestsError, |
@@ -132,26 +124,6 @@ class AppLayout extends Component { | |||
132 | {sidebar} | 124 | {sidebar} |
133 | <div className="app__service"> | 125 | <div className="app__service"> |
134 | <WorkspaceSwitchingIndicator /> | 126 | <WorkspaceSwitchingIndicator /> |
135 | {news.length > 0 && | ||
136 | news.map(item => ( | ||
137 | <InfoBar | ||
138 | key={item.id} | ||
139 | position="top" | ||
140 | type={item.type} | ||
141 | sticky={item.sticky} | ||
142 | onHide={() => removeNewsItem({ newsId: item.id })} | ||
143 | > | ||
144 | <span | ||
145 | dangerouslySetInnerHTML={createMarkup(item.message)} | ||
146 | onClick={event => { | ||
147 | const { target } = event; | ||
148 | if (target && target.hasAttribute('data-is-news-cta')) { | ||
149 | removeNewsItem({ newsId: item.id }); | ||
150 | } | ||
151 | }} | ||
152 | /> | ||
153 | </InfoBar> | ||
154 | ))} | ||
155 | {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( | 127 | {!areRequiredRequestsSuccessful && showRequiredRequestsError && ( |
156 | <InfoBar | 128 | <InfoBar |
157 | type="danger" | 129 | type="danger" |
@@ -176,19 +148,22 @@ class AppLayout extends Component { | |||
176 | {intl.formatMessage(messages.authRequestFailed)} | 148 | {intl.formatMessage(messages.authRequestFailed)} |
177 | </InfoBar> | 149 | </InfoBar> |
178 | )} | 150 | )} |
179 | {showServicesUpdatedInfoBar && this.state.shouldShowServicesUpdatedInfoBar && ( | 151 | {showServicesUpdatedInfoBar && |
180 | <InfoBar | 152 | this.state.shouldShowServicesUpdatedInfoBar && ( |
181 | type="primary" | 153 | <InfoBar |
182 | ctaLabel={intl.formatMessage(messages.buttonReloadServices)} | 154 | type="primary" |
183 | onClick={reloadServicesAfterUpdate} | 155 | ctaLabel={intl.formatMessage(messages.buttonReloadServices)} |
184 | onHide={() => { | 156 | onClick={reloadServicesAfterUpdate} |
185 | this.setState({ shouldShowServicesUpdatedInfoBar: false }); | 157 | onHide={() => { |
186 | }} | 158 | this.setState({ |
187 | > | 159 | shouldShowServicesUpdatedInfoBar: false, |
188 | <span className="mdi mdi-power-plug" /> | 160 | }); |
189 | {intl.formatMessage(messages.servicesUpdated)} | 161 | }} |
190 | </InfoBar> | 162 | > |
191 | )} | 163 | <span className="mdi mdi-power-plug" /> |
164 | {intl.formatMessage(messages.servicesUpdated)} | ||
165 | </InfoBar> | ||
166 | )} | ||
192 | {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( | 167 | {appUpdateIsDownloaded && this.state.shouldShowAppUpdateInfoBar && ( |
193 | <AppUpdateInfoBar | 168 | <AppUpdateInfoBar |
194 | onInstallUpdate={installAppUpdate} | 169 | onInstallUpdate={installAppUpdate} |
diff --git a/src/components/layout/Sidebar.js b/src/components/layout/Sidebar.js index 87233f7ca..fc33a3c58 100644 --- a/src/components/layout/Sidebar.js +++ b/src/components/layout/Sidebar.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import ReactTooltip from 'react-tooltip'; | 3 | import ReactTooltip from 'react-tooltip'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
@@ -177,7 +177,7 @@ class Sidebar extends Component { | |||
177 | > | 177 | > |
178 | <i className="mdi mdi-check-all" /> | 178 | <i className="mdi mdi-check-all" /> |
179 | </button> | 179 | </button> |
180 | ) : null} | 180 | ) : null} |
181 | {workspaceStore.isFeatureEnabled ? ( | 181 | {workspaceStore.isFeatureEnabled ? ( |
182 | <button | 182 | <button |
183 | type="button" | 183 | type="button" |
@@ -243,7 +243,7 @@ class Sidebar extends Component { | |||
243 | this.props.stores.app.updateStatusTypes.AVAILABLE || | 243 | this.props.stores.app.updateStatusTypes.AVAILABLE || |
244 | this.props.stores.app.updateStatus === | 244 | this.props.stores.app.updateStatus === |
245 | this.props.stores.app.updateStatusTypes.DOWNLOADED) && ( | 245 | this.props.stores.app.updateStatusTypes.DOWNLOADED) && ( |
246 | <span className="update-available">•</span> | 246 | <span className="update-available">•</span> |
247 | )} | 247 | )} |
248 | </button> | 248 | </button> |
249 | {this.state.tooltipEnabled && ( | 249 | {this.state.tooltipEnabled && ( |
diff --git a/src/components/services/content/ConnectionLostBanner.js b/src/components/services/content/ConnectionLostBanner.js index 423edb3c7..5111a081a 100644 --- a/src/components/services/content/ConnectionLostBanner.js +++ b/src/components/services/content/ConnectionLostBanner.js | |||
@@ -1,13 +1,12 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { createRef, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
7 | 6 | ||
8 | import { mdiAlert } from '@mdi/js'; | 7 | import { mdiAlert } from '@mdi/js'; |
9 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; | 8 | import { LIVE_API_FERDI_WEBSITE } from '../../../config'; |
10 | // import { Button } from '@meetfranz/forms'; | 9 | import { Icon } from '../../ui/icon'; |
11 | 10 | ||
12 | const messages = defineMessages({ | 11 | const messages = defineMessages({ |
13 | text: { | 12 | text: { |
@@ -78,7 +77,7 @@ class ConnectionLostBanner extends Component { | |||
78 | reload: PropTypes.func.isRequired, | 77 | reload: PropTypes.func.isRequired, |
79 | }; | 78 | }; |
80 | 79 | ||
81 | inputRef = React.createRef(); | 80 | inputRef = createRef(); |
82 | 81 | ||
83 | render() { | 82 | render() { |
84 | const { classes, name, reload } = this.props; | 83 | const { classes, name, reload } = this.props; |
diff --git a/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js b/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js index b00db8c3f..5c93de80f 100644 --- a/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js +++ b/src/components/services/content/ErrorHandlers/WebviewErrorHandler.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/services/content/ErrorHandlers/styles.js b/src/components/services/content/ErrorHandlers/styles.ts index 72d62f5e3..9e2509ee5 100644 --- a/src/components/services/content/ErrorHandlers/styles.js +++ b/src/components/services/content/ErrorHandlers/styles.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | export default (theme) => ({ | 1 | export default theme => ({ |
2 | component: { | 2 | component: { |
3 | left: 0, | 3 | left: 0, |
4 | position: 'absolute', | 4 | position: 'absolute', |
diff --git a/src/components/services/content/ServiceDisabled.js b/src/components/services/content/ServiceDisabled.js index e59ed58bd..476b23235 100644 --- a/src/components/services/content/ServiceDisabled.js +++ b/src/components/services/content/ServiceDisabled.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/services/content/ServiceView.js b/src/components/services/content/ServiceView.js index 81401b1d2..1bc1fbf5f 100644 --- a/src/components/services/content/ServiceView.js +++ b/src/components/services/content/ServiceView.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import { Component, Fragment } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { autorun } from 'mobx'; | 3 | import { autorun } from 'mobx'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
@@ -123,7 +123,7 @@ class ServiceView extends Component { | |||
123 | service.isFirstLoad && | 123 | service.isFirstLoad && |
124 | !service.isServiceAccessRestricted && ( | 124 | !service.isServiceAccessRestricted && ( |
125 | <WebviewLoader loaded={false} name={service.name} /> | 125 | <WebviewLoader loaded={false} name={service.name} /> |
126 | )} | 126 | )} |
127 | {service.isError && ( | 127 | {service.isError && ( |
128 | <WebviewErrorHandler | 128 | <WebviewErrorHandler |
129 | name={service.recipe.name} | 129 | name={service.recipe.name} |
diff --git a/src/components/services/content/ServiceWebview.js b/src/components/services/content/ServiceWebview.js index d3170be53..185d41175 100644 --- a/src/components/services/content/ServiceWebview.js +++ b/src/components/services/content/ServiceWebview.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { observable, reaction } from 'mobx'; | 4 | import { observable, reaction } from 'mobx'; |
@@ -24,12 +24,10 @@ class ServiceWebview extends Component { | |||
24 | super(props); | 24 | super(props); |
25 | 25 | ||
26 | reaction( | 26 | reaction( |
27 | () => ( | 27 | () => this.webview, |
28 | this.webview | ||
29 | ), | ||
30 | () => { | 28 | () => { |
31 | if (this.webview && this.webview.view) { | 29 | if (this.webview && this.webview.view) { |
32 | this.webview.view.addEventListener('console-message', (e) => { | 30 | this.webview.view.addEventListener('console-message', e => { |
33 | debug('Service logged a message:', e.message); | 31 | debug('Service logged a message:', e.message); |
34 | }); | 32 | }); |
35 | } | 33 | } |
@@ -55,20 +53,26 @@ class ServiceWebview extends Component { | |||
55 | }; | 53 | }; |
56 | 54 | ||
57 | render() { | 55 | render() { |
58 | const { | 56 | const { service, setWebviewReference, isSpellcheckerEnabled } = this.props; |
59 | service, | ||
60 | setWebviewReference, | ||
61 | isSpellcheckerEnabled, | ||
62 | } = this.props; | ||
63 | 57 | ||
64 | const preloadScript = join(__dirname, '..', '..', '..', 'webview', 'recipe.js'); | 58 | const preloadScript = join( |
59 | __dirname, | ||
60 | '..', | ||
61 | '..', | ||
62 | '..', | ||
63 | 'webview', | ||
64 | 'recipe.js', | ||
65 | ); | ||
65 | 66 | ||
66 | return ( | 67 | return ( |
67 | <ElectronWebView | 68 | <ElectronWebView |
68 | ref={(webview) => { | 69 | ref={webview => { |
69 | this.webview = webview; | 70 | this.webview = webview; |
70 | if (webview && webview.view) { | 71 | if (webview && webview.view) { |
71 | webview.view.addEventListener('did-stop-loading', this.refocusWebview); | 72 | webview.view.addEventListener( |
73 | 'did-stop-loading', | ||
74 | this.refocusWebview, | ||
75 | ); | ||
72 | } | 76 | } |
73 | }} | 77 | }} |
74 | autosize | 78 | autosize |
@@ -83,10 +87,14 @@ class ServiceWebview extends Component { | |||
83 | }} | 87 | }} |
84 | onUpdateTargetUrl={this.updateTargetUrl} | 88 | onUpdateTargetUrl={this.updateTargetUrl} |
85 | useragent={service.userAgent} | 89 | useragent={service.userAgent} |
86 | disablewebsecurity={service.recipe.disablewebsecurity ? true : undefined} | 90 | disablewebsecurity={ |
91 | service.recipe.disablewebsecurity ? true : undefined | ||
92 | } | ||
87 | allowpopups | 93 | allowpopups |
88 | nodeintegration | 94 | nodeintegration |
89 | webpreferences={`spellcheck=${isSpellcheckerEnabled ? 1 : 0}, contextIsolation=1, enableRemoteModule=1`} | 95 | webpreferences={`spellcheck=${ |
96 | isSpellcheckerEnabled ? 1 : 0 | ||
97 | }, contextIsolation=1, enableRemoteModule=1`} | ||
90 | /> | 98 | /> |
91 | ); | 99 | ); |
92 | } | 100 | } |
diff --git a/src/components/services/content/Services.js b/src/components/services/content/Services.js index fb43fb816..1edf31bd3 100644 --- a/src/components/services/content/Services.js +++ b/src/components/services/content/Services.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; |
4 | import { Link } from 'react-router'; | 4 | import { Link } from 'react-router'; |
diff --git a/src/components/services/content/WebviewCrashHandler.js b/src/components/services/content/WebviewCrashHandler.js index a332602be..3607435b3 100644 --- a/src/components/services/content/WebviewCrashHandler.js +++ b/src/components/services/content/WebviewCrashHandler.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/services/tabs/TabBarSortableList.js b/src/components/services/tabs/TabBarSortableList.js index 1a389991d..69a12e982 100644 --- a/src/components/services/tabs/TabBarSortableList.js +++ b/src/components/services/tabs/TabBarSortableList.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 2 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { SortableContainer } from 'react-sortable-hoc'; | 4 | import { SortableContainer } from 'react-sortable-hoc'; |
@@ -22,7 +22,7 @@ class TabBarSortableList extends Component { | |||
22 | wakeUpService: PropTypes.func.isRequired, | 22 | wakeUpService: PropTypes.func.isRequired, |
23 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, | 23 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, |
24 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, | 24 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, |
25 | } | 25 | }; |
26 | 26 | ||
27 | render() { | 27 | render() { |
28 | const { | 28 | const { |
@@ -43,9 +43,7 @@ class TabBarSortableList extends Component { | |||
43 | } = this.props; | 43 | } = this.props; |
44 | 44 | ||
45 | return ( | 45 | return ( |
46 | <ul | 46 | <ul className="tabs"> |
47 | className="tabs" | ||
48 | > | ||
49 | {services.map((service, index) => ( | 47 | {services.map((service, index) => ( |
50 | <TabItem | 48 | <TabItem |
51 | key={service.id} | 49 | key={service.id} |
@@ -54,7 +52,9 @@ class TabBarSortableList extends Component { | |||
54 | index={index} | 52 | index={index} |
55 | shortcutIndex={index + 1} | 53 | shortcutIndex={index + 1} |
56 | reload={() => reload({ serviceId: service.id })} | 54 | reload={() => reload({ serviceId: service.id })} |
57 | toggleNotifications={() => toggleNotifications({ serviceId: service.id })} | 55 | toggleNotifications={() => |
56 | toggleNotifications({ serviceId: service.id }) | ||
57 | } | ||
58 | toggleAudio={() => toggleAudio({ serviceId: service.id })} | 58 | toggleAudio={() => toggleAudio({ serviceId: service.id })} |
59 | toggleDarkMode={() => toggleDarkMode({ serviceId: service.id })} | 59 | toggleDarkMode={() => toggleDarkMode({ serviceId: service.id })} |
60 | deleteService={() => deleteService({ serviceId: service.id })} | 60 | deleteService={() => deleteService({ serviceId: service.id })} |
diff --git a/src/components/services/tabs/TabItem.js b/src/components/services/tabs/TabItem.js index 2474682df..ed8430b89 100644 --- a/src/components/services/tabs/TabItem.js +++ b/src/components/services/tabs/TabItem.js | |||
@@ -1,17 +1,18 @@ | |||
1 | import { Menu, dialog, app } from '@electron/remote'; | 1 | import { Menu, dialog, app } from '@electron/remote'; |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl } from 'react-intl'; |
4 | import PropTypes from 'prop-types'; | 4 | import PropTypes from 'prop-types'; |
5 | import { observer } from 'mobx-react'; | 5 | import { inject, observer } from 'mobx-react'; |
6 | import classnames from 'classnames'; | 6 | import classnames from 'classnames'; |
7 | import { SortableElement } from 'react-sortable-hoc'; | 7 | import { SortableElement } from 'react-sortable-hoc'; |
8 | import injectSheet from 'react-jss'; | 8 | import injectSheet from 'react-jss'; |
9 | import ms from 'ms'; | 9 | import ms from 'ms'; |
10 | 10 | ||
11 | import { observable, autorun } from 'mobx'; | 11 | import { observable, autorun, reaction } from 'mobx'; |
12 | import ServiceModel from '../../../models/Service'; | 12 | import ServiceModel from '../../../models/Service'; |
13 | import { cmdOrCtrlShortcutKey } from '../../../environment'; | 13 | import { cmdOrCtrlShortcutKey } from '../../../environment'; |
14 | import globalMessages from '../../../i18n/globalMessages'; | 14 | import globalMessages from '../../../i18n/globalMessages'; |
15 | import SettingsStore from '../../../stores/SettingsStore'; | ||
15 | 16 | ||
16 | const IS_SERVICE_DEBUGGING_ENABLED = ( | 17 | const IS_SERVICE_DEBUGGING_ENABLED = ( |
17 | localStorage.getItem('debug') || '' | 18 | localStorage.getItem('debug') || '' |
@@ -112,6 +113,7 @@ const styles = { | |||
112 | }; | 113 | }; |
113 | 114 | ||
114 | @injectSheet(styles) | 115 | @injectSheet(styles) |
116 | @inject('stores') | ||
115 | @observer | 117 | @observer |
116 | class TabItem extends Component { | 118 | class TabItem extends Component { |
117 | static propTypes = { | 119 | static propTypes = { |
@@ -131,6 +133,9 @@ class TabItem extends Component { | |||
131 | wakeUpService: PropTypes.func.isRequired, | 133 | wakeUpService: PropTypes.func.isRequired, |
132 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, | 134 | showMessageBadgeWhenMutedSetting: PropTypes.bool.isRequired, |
133 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, | 135 | showMessageBadgesEvenWhenMuted: PropTypes.bool.isRequired, |
136 | stores: PropTypes.shape({ | ||
137 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | ||
138 | }).isRequired, | ||
134 | }; | 139 | }; |
135 | 140 | ||
136 | @observable isPolled = false; | 141 | @observable isPolled = false; |
@@ -142,37 +147,37 @@ class TabItem extends Component { | |||
142 | this.state = { | 147 | this.state = { |
143 | showShortcutIndex: false, | 148 | showShortcutIndex: false, |
144 | }; | 149 | }; |
150 | |||
151 | reaction( | ||
152 | () => this.props.stores.settings.app.enableLongPressServiceHint, | ||
153 | () => { | ||
154 | this.checkForLongPress( | ||
155 | this.props.stores.settings.app.enableLongPressServiceHint, | ||
156 | ); | ||
157 | }, | ||
158 | ); | ||
145 | } | 159 | } |
146 | 160 | ||
147 | handleShowShortcutIndex = () => { | 161 | handleShortcutIndex = (event, showShortcutIndex = true) => { |
148 | this.setState({ showShortcutIndex: true }); | 162 | if (event.key === 'Shift') { |
163 | this.setState({ showShortcutIndex }); | ||
164 | } | ||
149 | }; | 165 | }; |
150 | 166 | ||
151 | checkForLongPress = () => { | 167 | checkForLongPress = enableLongPressServiceHint => { |
152 | let longpressDelay = null; | 168 | if (enableLongPressServiceHint) { |
153 | const longpressDelayDuration = 1000; | 169 | document.addEventListener('keydown', e => { |
154 | 170 | this.handleShortcutIndex(e); | |
155 | document.addEventListener( | 171 | }); |
156 | 'keydown', | ||
157 | e => { | ||
158 | if (e.ctrlKey || e.metaKey) { | ||
159 | longpressDelay = setTimeout( | ||
160 | this.handleShowShortcutIndex, | ||
161 | longpressDelayDuration, | ||
162 | ); | ||
163 | } | ||
164 | }, | ||
165 | { capture: true }, | ||
166 | ); | ||
167 | 172 | ||
168 | document.addEventListener('keyup', () => { | 173 | document.addEventListener('keyup', e => { |
169 | clearTimeout(longpressDelay); | 174 | this.handleShortcutIndex(e, false); |
170 | this.setState({ showShortcutIndex: false }); | 175 | }); |
171 | }); | 176 | } |
172 | }; | 177 | }; |
173 | 178 | ||
174 | componentDidMount() { | 179 | componentDidMount() { |
175 | const { service } = this.props; | 180 | const { service, stores } = this.props; |
176 | 181 | ||
177 | if (IS_SERVICE_DEBUGGING_ENABLED) { | 182 | if (IS_SERVICE_DEBUGGING_ENABLED) { |
178 | autorun(() => { | 183 | autorun(() => { |
@@ -194,7 +199,7 @@ class TabItem extends Component { | |||
194 | }); | 199 | }); |
195 | } | 200 | } |
196 | 201 | ||
197 | this.checkForLongPress(); | 202 | this.checkForLongPress(stores.settings.app.enableLongPressServiceHint); |
198 | } | 203 | } |
199 | 204 | ||
200 | render() { | 205 | render() { |
@@ -364,7 +369,7 @@ class TabItem extends Component { | |||
364 | /> | 369 | /> |
365 | </> | 370 | </> |
366 | )} | 371 | )} |
367 | {shortcutIndex && this.state.showShortcutIndex && ( | 372 | {shortcutIndex <= 9 && this.state.showShortcutIndex && ( |
368 | <span className="tab-item__shortcut-index">{shortcutIndex}</span> | 373 | <span className="tab-item__shortcut-index">{shortcutIndex}</span> |
369 | )} | 374 | )} |
370 | </li> | 375 | </li> |
diff --git a/src/components/services/tabs/Tabbar.js b/src/components/services/tabs/Tabbar.js index a77799819..4ab0e8611 100644 --- a/src/components/services/tabs/Tabbar.js +++ b/src/components/services/tabs/Tabbar.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/components/settings/SettingsLayout.js b/src/components/settings/SettingsLayout.js index 71250bd4d..be11fdb8e 100644 --- a/src/components/settings/SettingsLayout.js +++ b/src/components/settings/SettingsLayout.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/settings/account/AccountDashboard.js b/src/components/settings/account/AccountDashboard.js index 544821e9a..6c489e64b 100644 --- a/src/components/settings/account/AccountDashboard.js +++ b/src/components/settings/account/AccountDashboard.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import ReactTooltip from 'react-tooltip'; | 5 | import ReactTooltip from 'react-tooltip'; |
6 | import { H1, H2 } from '@meetfranz/ui'; | 6 | import { H1, H2 } from '../../ui/headline'; |
7 | 7 | ||
8 | import Loader from '../../ui/Loader'; | 8 | import Loader from '../../ui/Loader'; |
9 | import Button from '../../ui/Button'; | 9 | import Button from '../../ui/Button'; |
diff --git a/src/components/settings/navigation/SettingsNavigation.js b/src/components/settings/navigation/SettingsNavigation.js index 72c7faa66..18a71fdeb 100644 --- a/src/components/settings/navigation/SettingsNavigation.js +++ b/src/components/settings/navigation/SettingsNavigation.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl } from 'react-intl'; |
4 | import { inject, observer } from 'mobx-react'; | 4 | import { inject, observer } from 'mobx-react'; |
diff --git a/src/components/settings/recipes/RecipeItem.js b/src/components/settings/recipes/RecipeItem.js index ca188aa99..1e910e6dc 100644 --- a/src/components/settings/recipes/RecipeItem.js +++ b/src/components/settings/recipes/RecipeItem.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/components/settings/recipes/RecipesDashboard.js b/src/components/settings/recipes/RecipesDashboard.js index 44f5bc39a..bdb6f3ca0 100644 --- a/src/components/settings/recipes/RecipesDashboard.js +++ b/src/components/settings/recipes/RecipesDashboard.js | |||
@@ -1,12 +1,14 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | 6 | ||
7 | import { Button, Input } from '@meetfranz/forms'; | ||
8 | import injectSheet from 'react-jss'; | 7 | import injectSheet from 'react-jss'; |
9 | import { H3, H2 } from '@meetfranz/ui'; | 8 | |
9 | import { Button } from '../../ui/button/index'; | ||
10 | import { Input } from '../../ui/input/index'; | ||
11 | import { H3, H2 } from '../../ui/headline'; | ||
10 | import SearchInput from '../../ui/SearchInput'; | 12 | import SearchInput from '../../ui/SearchInput'; |
11 | import Infobox from '../../ui/Infobox'; | 13 | import Infobox from '../../ui/Infobox'; |
12 | import RecipeItem from './RecipeItem'; | 14 | import RecipeItem from './RecipeItem'; |
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js index 22089ec45..b040e6cc2 100644 --- a/src/components/settings/services/EditServiceForm.js +++ b/src/components/settings/services/EditServiceForm.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { Link } from 'react-router'; | 4 | import { Link } from 'react-router'; |
@@ -356,6 +356,7 @@ class EditServiceForm extends Component { | |||
356 | <p className="settings__help indented__help"> | 356 | <p className="settings__help indented__help"> |
357 | {intl.formatMessage(messages.isHibernationEnabledInfo)} | 357 | {intl.formatMessage(messages.isHibernationEnabledInfo)} |
358 | </p> | 358 | </p> |
359 | <Toggle field={form.$('isWakeUpEnabled')} /> | ||
359 | <Toggle field={form.$('isDarkModeEnabled')} /> | 360 | <Toggle field={form.$('isDarkModeEnabled')} /> |
360 | {form.$('isDarkModeEnabled').value && ( | 361 | {form.$('isDarkModeEnabled').value && ( |
361 | <> | 362 | <> |
diff --git a/src/components/settings/services/ServiceError.js b/src/components/settings/services/ServiceError.js index d16d76db2..6dd53a102 100644 --- a/src/components/settings/services/ServiceError.js +++ b/src/components/settings/services/ServiceError.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
3 | import { Link } from 'react-router'; | 3 | import { Link } from 'react-router'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/settings/services/ServiceItem.js b/src/components/settings/services/ServiceItem.js index 4916e4ecc..e08b9af1f 100644 --- a/src/components/settings/services/ServiceItem.js +++ b/src/components/settings/services/ServiceItem.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { defineMessages, injectIntl } from 'react-intl'; | 3 | import { defineMessages, injectIntl } from 'react-intl'; |
4 | import ReactTooltip from 'react-tooltip'; | 4 | import ReactTooltip from 'react-tooltip'; |
diff --git a/src/components/settings/services/ServicesDashboard.js b/src/components/settings/services/ServicesDashboard.js index bb52db97f..aae6eb855 100644 --- a/src/components/settings/services/ServicesDashboard.js +++ b/src/components/settings/services/ServicesDashboard.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { Link } from 'react-router'; | 4 | import { Link } from 'react-router'; |
diff --git a/src/components/settings/settings/EditSettingsForm.js b/src/components/settings/settings/EditSettingsForm.js index 123ab4c2d..948e9ccd5 100644 --- a/src/components/settings/settings/EditSettingsForm.js +++ b/src/components/settings/settings/EditSettingsForm.js | |||
@@ -1,5 +1,5 @@ | |||
1 | import { systemPreferences } from '@electron/remote'; | 1 | import { systemPreferences } from '@electron/remote'; |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer } from 'mobx-react'; | 4 | import { observer } from 'mobx-react'; |
5 | import prettyBytes from 'pretty-bytes'; | 5 | import prettyBytes from 'pretty-bytes'; |
@@ -12,13 +12,17 @@ import ToggleRaw from '../../ui/ToggleRaw'; | |||
12 | import Select from '../../ui/Select'; | 12 | import Select from '../../ui/Select'; |
13 | import Input from '../../ui/Input'; | 13 | import Input from '../../ui/Input'; |
14 | 14 | ||
15 | import { DEFAULT_APP_SETTINGS, FRANZ_TRANSLATION, GITHUB_FRANZ_URL } from '../../../config'; | ||
16 | import { | 15 | import { |
17 | isMac, | 16 | DEFAULT_APP_SETTINGS, |
18 | isWindows, | 17 | FRANZ_TRANSLATION, |
19 | lockFerdiShortcutKey, | 18 | GITHUB_FRANZ_URL, |
20 | } from '../../../environment'; | 19 | } from '../../../config'; |
21 | import { ferdiVersion, userDataPath, userDataRecipesPath } from '../../../environment-remote'; | 20 | import { isMac, isWindows, lockFerdiShortcutKey } from '../../../environment'; |
21 | import { | ||
22 | ferdiVersion, | ||
23 | userDataPath, | ||
24 | userDataRecipesPath, | ||
25 | } from '../../../environment-remote'; | ||
22 | import { openPath } from '../../../helpers/url-helpers'; | 26 | import { openPath } from '../../../helpers/url-helpers'; |
23 | import globalMessages from '../../../i18n/globalMessages'; | 27 | import globalMessages from '../../../i18n/globalMessages'; |
24 | 28 | ||
@@ -550,6 +554,7 @@ class EditSettingsForm extends Component { | |||
550 | 554 | ||
551 | <Hr /> | 555 | <Hr /> |
552 | <Select field={form.$('iconSize')} /> | 556 | <Select field={form.$('iconSize')} /> |
557 | <Toggle field={form.$('enableLongPressServiceHint')} /> | ||
553 | 558 | ||
554 | <Hr /> | 559 | <Hr /> |
555 | 560 | ||
@@ -673,6 +678,7 @@ class EditSettingsForm extends Component { | |||
673 | {this.state.activeSetttingsTab === 'advanced' && ( | 678 | {this.state.activeSetttingsTab === 'advanced' && ( |
674 | <div> | 679 | <div> |
675 | <Toggle field={form.$('enableGPUAcceleration')} /> | 680 | <Toggle field={form.$('enableGPUAcceleration')} /> |
681 | <Toggle field={form.$('enableGlobalHideShortcut')} /> | ||
676 | <p className="settings__help indented__help"> | 682 | <p className="settings__help indented__help"> |
677 | {intl.formatMessage(messages.appRestartRequired)} | 683 | {intl.formatMessage(messages.appRestartRequired)} |
678 | </p> | 684 | </p> |
diff --git a/src/components/settings/supportFerdi/SupportFerdiDashboard.js b/src/components/settings/supportFerdi/SupportFerdiDashboard.js deleted file mode 100644 index f24e4bd62..000000000 --- a/src/components/settings/supportFerdi/SupportFerdiDashboard.js +++ /dev/null | |||
@@ -1,234 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import { defineMessages, injectIntl } from 'react-intl'; | ||
3 | import { BrowserWindow } from '@electron/remote'; | ||
4 | import InfoBar from '../../ui/InfoBar'; | ||
5 | |||
6 | const messages = defineMessages({ | ||
7 | headline: { | ||
8 | id: 'settings.supportFerdi.headline', | ||
9 | defaultMessage: 'About Ferdi', | ||
10 | }, | ||
11 | title: { | ||
12 | id: 'settings.supportFerdi.title', | ||
13 | defaultMessage: 'Do you like Ferdi?', | ||
14 | }, | ||
15 | aboutIntro: { | ||
16 | id: 'settings.supportFerdi.aboutIntro', | ||
17 | defaultMessage: | ||
18 | '<p>Ferdi is an open-source and a community-lead application.</p><p>Thanks to the people who make this possbile:</p>', | ||
19 | }, | ||
20 | textListContributors: { | ||
21 | id: 'settings.supportFerdi.textListContributors', | ||
22 | defaultMessage: 'Full list of contributors', | ||
23 | }, | ||
24 | textListContributorsHere: { | ||
25 | id: 'settings.supportFerdi.textListContributorsHere', | ||
26 | defaultMessage: 'here', | ||
27 | }, | ||
28 | textVolunteers: { | ||
29 | id: 'settings.supportFerdi.textVolunteers', | ||
30 | defaultMessage: | ||
31 | 'The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.', | ||
32 | }, | ||
33 | textSupportWelcome: { | ||
34 | id: 'settings.supportFerdi.textSupportWelcome', | ||
35 | defaultMessage: | ||
36 | 'Support is always welcome. You can find a list of the help we need', | ||
37 | }, | ||
38 | textSupportWelcomeHere: { | ||
39 | id: 'settings.supportFerdi.textSupportWelcomeHere', | ||
40 | defaultMessage: 'here', | ||
41 | }, | ||
42 | textExpenses: { | ||
43 | id: 'settings.supportFerdi.textExpenses', | ||
44 | defaultMessage: | ||
45 | 'While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our', | ||
46 | }, | ||
47 | textOpenCollective: { | ||
48 | id: 'settings.supportFerdi.textOpenCollective', | ||
49 | defaultMessage: 'Open Collective', | ||
50 | }, | ||
51 | textDonation: { | ||
52 | id: 'settings.supportFerdi.textDonation', | ||
53 | defaultMessage: | ||
54 | 'If you feel like supporting Ferdi development with a donation, you can do so on both,', | ||
55 | }, | ||
56 | textDonationAnd: { | ||
57 | id: 'settings.supportFerdi.textDonationAnd', | ||
58 | defaultMessage: 'and', | ||
59 | }, | ||
60 | textGitHubSponsors: { | ||
61 | id: 'settings.supportFerdi.textGitHubSponsors', | ||
62 | defaultMessage: 'GitHub Sponsors', | ||
63 | }, | ||
64 | openSurvey: { | ||
65 | id: 'settings.supportFerdi.openSurvey', | ||
66 | defaultMessage: 'Open survey', | ||
67 | }, | ||
68 | bannerText: { | ||
69 | id: 'settings.supportFerdi.bannerText', | ||
70 | defaultMessage: 'Do you want to help us improve Ferdi?', | ||
71 | }, | ||
72 | }); | ||
73 | |||
74 | class SupportFerdiDashboard extends Component { | ||
75 | openSurveyWindow() { | ||
76 | let win = new BrowserWindow({ width: 670, height: 400 }); | ||
77 | win.on('closed', () => { | ||
78 | win = null; | ||
79 | }); | ||
80 | |||
81 | win.loadURL('https://rp28.typeform.com/to/E3phJT'); | ||
82 | } | ||
83 | |||
84 | render() { | ||
85 | const { intl } = this.props; | ||
86 | |||
87 | const aboutIntro = intl.formatMessage(messages.aboutIntro); | ||
88 | |||
89 | return ( | ||
90 | <div className="settings__main"> | ||
91 | <div className="settings__header"> | ||
92 | <span className="settings__header-item"> | ||
93 | {intl.formatMessage(messages.headline)} | ||
94 | </span> | ||
95 | </div> | ||
96 | <div className="settings__body"> | ||
97 | <h1>{intl.formatMessage(messages.title)}</h1> | ||
98 | <div> | ||
99 | <p className="settings__support-badges"> | ||
100 | <a | ||
101 | href="https://github.com/getferdi/ferdi" | ||
102 | target="_blank" | ||
103 | rel="noreferrer" | ||
104 | > | ||
105 | <img | ||
106 | alt="GitHub Stars" | ||
107 | src="https://img.shields.io/github/stars/getferdi/ferdi?style=social" | ||
108 | /> | ||
109 | </a> | ||
110 | <a | ||
111 | href="https://twitter.com/getferdi/" | ||
112 | target="_blank" | ||
113 | rel="noreferrer" | ||
114 | > | ||
115 | <img | ||
116 | alt="Twitter Follow" | ||
117 | src="https://img.shields.io/twitter/follow/getferdi?label=Follow&style=social" | ||
118 | /> | ||
119 | </a> | ||
120 | <a | ||
121 | href="https://opencollective.com/getferdi#section-contributors" | ||
122 | target="_blank" | ||
123 | rel="noreferrer" | ||
124 | > | ||
125 | <img | ||
126 | alt="Open Collective backers" | ||
127 | src="https://img.shields.io/opencollective/backers/getferdi?logo=open-collective" | ||
128 | /> | ||
129 | </a> | ||
130 | <a | ||
131 | href="https://opencollective.com/getferdi#section-contributors" | ||
132 | target="_blank" | ||
133 | rel="noreferrer" | ||
134 | > | ||
135 | <img | ||
136 | alt="Open Collective sponsors" | ||
137 | src="https://img.shields.io/opencollective/sponsors/getferdi?logo=open-collective" | ||
138 | /> | ||
139 | </a> | ||
140 | </p> | ||
141 | <span dangerouslySetInnerHTML={{ __html: aboutIntro }} /> | ||
142 | <br /> | ||
143 | <br /> | ||
144 | <p> | ||
145 | <a href="#contributors-via-opencollective"> | ||
146 | <img | ||
147 | alt="GitHub contributors (non-exhaustive)" | ||
148 | width="100%" | ||
149 | src="https://opencollective.com/getferdi/contributors.svg?width=642&button=false" | ||
150 | /> | ||
151 | </a> | ||
152 | </p> | ||
153 | <p> | ||
154 | {intl.formatMessage(messages.textListContributors)} | ||
155 | <a | ||
156 | href="https://github.com/getferdi/ferdi#contributors-" | ||
157 | target="_blank" | ||
158 | className="link" | ||
159 | rel="noreferrer" | ||
160 | > | ||
161 | {' '} | ||
162 | {intl.formatMessage(messages.textListContributorsHere)} | ||
163 | <i className="mdi mdi-open-in-new" /> | ||
164 | </a> | ||
165 | <br /> | ||
166 | <br /> | ||
167 | </p> | ||
168 | <p>{intl.formatMessage(messages.textVolunteers)}</p> | ||
169 | <p> | ||
170 | {intl.formatMessage(messages.textSupportWelcome)} | ||
171 | <a | ||
172 | href="https://help.getferdi.com/general/support" | ||
173 | target="_blank" | ||
174 | className="link" | ||
175 | rel="noreferrer" | ||
176 | > | ||
177 | {' '} | ||
178 | {intl.formatMessage(messages.textSupportWelcomeHere)} | ||
179 | <i className="mdi mdi-open-in-new" /> | ||
180 | </a> | ||
181 | </p> | ||
182 | <p> | ||
183 | {intl.formatMessage(messages.textExpenses)} | ||
184 | <a | ||
185 | href="https://opencollective.com/getferdi#section-budget" | ||
186 | target="_blank" | ||
187 | className="link" | ||
188 | rel="noreferrer" | ||
189 | > | ||
190 | {' '} | ||
191 | {intl.formatMessage(messages.textOpenCollective)} | ||
192 | <i className="mdi mdi-open-in-new" /> | ||
193 | </a> | ||
194 | </p> | ||
195 | <p> | ||
196 | {intl.formatMessage(messages.textDonation)} | ||
197 | <a | ||
198 | href="https://opencollective.com/getferdi#section-contribute" | ||
199 | target="_blank" | ||
200 | className="link" | ||
201 | rel="noreferrer" | ||
202 | > | ||
203 | {' '} | ||
204 | {intl.formatMessage(messages.textOpenCollective)} | ||
205 | <i className="mdi mdi-open-in-new" /> | ||
206 | </a>{' '} | ||
207 | {intl.formatMessage(messages.textDonationAnd)} | ||
208 | <a | ||
209 | href="https://github.com/sponsors/getferdi" | ||
210 | target="_blank" | ||
211 | className="link" | ||
212 | rel="noreferrer" | ||
213 | > | ||
214 | {' '} | ||
215 | {intl.formatMessage(messages.textGitHubSponsors)} | ||
216 | <i className="mdi mdi-open-in-new" /> | ||
217 | </a> | ||
218 | </p> | ||
219 | </div> | ||
220 | </div> | ||
221 | <InfoBar | ||
222 | sticky | ||
223 | type="primary" | ||
224 | ctaLabel={intl.formatMessage(messages.openSurvey)} | ||
225 | onClick={this.openSurveyWindow} | ||
226 | > | ||
227 | {intl.formatMessage(messages.bannerText)} | ||
228 | </InfoBar> | ||
229 | </div> | ||
230 | ); | ||
231 | } | ||
232 | } | ||
233 | |||
234 | export default injectIntl(SupportFerdiDashboard); | ||
diff --git a/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx b/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx new file mode 100644 index 000000000..505c49812 --- /dev/null +++ b/src/components/settings/supportFerdi/SupportFerdiDashboard.tsx | |||
@@ -0,0 +1,232 @@ | |||
1 | import { defineMessages, useIntl } from 'react-intl'; | ||
2 | import { BrowserWindow } from '@electron/remote'; | ||
3 | import InfoBar from '../../ui/InfoBar'; | ||
4 | |||
5 | const messages = defineMessages({ | ||
6 | headline: { | ||
7 | id: 'settings.supportFerdi.headline', | ||
8 | defaultMessage: 'About Ferdi', | ||
9 | }, | ||
10 | title: { | ||
11 | id: 'settings.supportFerdi.title', | ||
12 | defaultMessage: 'Do you like Ferdi?', | ||
13 | }, | ||
14 | aboutIntro: { | ||
15 | id: 'settings.supportFerdi.aboutIntro', | ||
16 | defaultMessage: | ||
17 | '<p>Ferdi is an open-source and a community-lead application.</p><p>Thanks to the people who make this possbile:</p>', | ||
18 | }, | ||
19 | textListContributors: { | ||
20 | id: 'settings.supportFerdi.textListContributors', | ||
21 | defaultMessage: 'Full list of contributors', | ||
22 | }, | ||
23 | textListContributorsHere: { | ||
24 | id: 'settings.supportFerdi.textListContributorsHere', | ||
25 | defaultMessage: 'here', | ||
26 | }, | ||
27 | textVolunteers: { | ||
28 | id: 'settings.supportFerdi.textVolunteers', | ||
29 | defaultMessage: | ||
30 | 'The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.', | ||
31 | }, | ||
32 | textSupportWelcome: { | ||
33 | id: 'settings.supportFerdi.textSupportWelcome', | ||
34 | defaultMessage: | ||
35 | 'Support is always welcome. You can find a list of the help we need', | ||
36 | }, | ||
37 | textSupportWelcomeHere: { | ||
38 | id: 'settings.supportFerdi.textSupportWelcomeHere', | ||
39 | defaultMessage: 'here', | ||
40 | }, | ||
41 | textExpenses: { | ||
42 | id: 'settings.supportFerdi.textExpenses', | ||
43 | defaultMessage: | ||
44 | 'While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our', | ||
45 | }, | ||
46 | textOpenCollective: { | ||
47 | id: 'settings.supportFerdi.textOpenCollective', | ||
48 | defaultMessage: 'Open Collective', | ||
49 | }, | ||
50 | textDonation: { | ||
51 | id: 'settings.supportFerdi.textDonation', | ||
52 | defaultMessage: | ||
53 | 'If you feel like supporting Ferdi development with a donation, you can do so on both,', | ||
54 | }, | ||
55 | textDonationAnd: { | ||
56 | id: 'settings.supportFerdi.textDonationAnd', | ||
57 | defaultMessage: 'and', | ||
58 | }, | ||
59 | textGitHubSponsors: { | ||
60 | id: 'settings.supportFerdi.textGitHubSponsors', | ||
61 | defaultMessage: 'GitHub Sponsors', | ||
62 | }, | ||
63 | openSurvey: { | ||
64 | id: 'settings.supportFerdi.openSurvey', | ||
65 | defaultMessage: 'Open survey', | ||
66 | }, | ||
67 | bannerText: { | ||
68 | id: 'settings.supportFerdi.bannerText', | ||
69 | defaultMessage: 'Do you want to help us improve Ferdi?', | ||
70 | }, | ||
71 | }); | ||
72 | |||
73 | const openSurveyWindow = () => { | ||
74 | let win = new BrowserWindow({ width: 670, height: 400 }); | ||
75 | win.on('closed', () => { | ||
76 | // @ts-expect-error Type 'null' is not assignable to type 'BrowserWindow'. | ||
77 | win = null; | ||
78 | }); | ||
79 | |||
80 | win.loadURL('https://rp28.typeform.com/to/E3phJT'); | ||
81 | }; | ||
82 | |||
83 | const SupportFerdiDashboard = () => { | ||
84 | const intl = useIntl(); | ||
85 | |||
86 | const aboutIntro = intl.formatMessage(messages.aboutIntro); | ||
87 | |||
88 | return ( | ||
89 | <div className="settings__main"> | ||
90 | <div className="settings__header"> | ||
91 | <span className="settings__header-item"> | ||
92 | {intl.formatMessage(messages.headline)} | ||
93 | </span> | ||
94 | </div> | ||
95 | <div className="settings__body"> | ||
96 | <h1>{intl.formatMessage(messages.title)}</h1> | ||
97 | <div> | ||
98 | <p className="settings__support-badges"> | ||
99 | <a | ||
100 | href="https://github.com/getferdi/ferdi" | ||
101 | target="_blank" | ||
102 | rel="noreferrer" | ||
103 | > | ||
104 | <img | ||
105 | alt="GitHub Stars" | ||
106 | src="https://img.shields.io/github/stars/getferdi/ferdi?style=social" | ||
107 | /> | ||
108 | </a> | ||
109 | <a | ||
110 | href="https://twitter.com/getferdi/" | ||
111 | target="_blank" | ||
112 | rel="noreferrer" | ||
113 | > | ||
114 | <img | ||
115 | alt="Twitter Follow" | ||
116 | src="https://img.shields.io/twitter/follow/getferdi?label=Follow&style=social" | ||
117 | /> | ||
118 | </a> | ||
119 | <a | ||
120 | href="https://opencollective.com/getferdi#section-contributors" | ||
121 | target="_blank" | ||
122 | rel="noreferrer" | ||
123 | > | ||
124 | <img | ||
125 | alt="Open Collective backers" | ||
126 | src="https://img.shields.io/opencollective/backers/getferdi?logo=open-collective" | ||
127 | /> | ||
128 | </a> | ||
129 | <a | ||
130 | href="https://opencollective.com/getferdi#section-contributors" | ||
131 | target="_blank" | ||
132 | rel="noreferrer" | ||
133 | > | ||
134 | <img | ||
135 | alt="Open Collective sponsors" | ||
136 | src="https://img.shields.io/opencollective/sponsors/getferdi?logo=open-collective" | ||
137 | /> | ||
138 | </a> | ||
139 | </p> | ||
140 | <span dangerouslySetInnerHTML={{ __html: aboutIntro }} /> | ||
141 | <br /> | ||
142 | <br /> | ||
143 | <p> | ||
144 | <a href="#contributors-via-opencollective"> | ||
145 | <img | ||
146 | alt="GitHub contributors (non-exhaustive)" | ||
147 | width="100%" | ||
148 | src="https://opencollective.com/getferdi/contributors.svg?width=642&button=false" | ||
149 | /> | ||
150 | </a> | ||
151 | </p> | ||
152 | <p> | ||
153 | {intl.formatMessage(messages.textListContributors)} | ||
154 | <a | ||
155 | href="https://github.com/getferdi/ferdi#contributors-" | ||
156 | target="_blank" | ||
157 | className="link" | ||
158 | rel="noreferrer" | ||
159 | > | ||
160 | {' '} | ||
161 | {intl.formatMessage(messages.textListContributorsHere)} | ||
162 | <i className="mdi mdi-open-in-new" /> | ||
163 | </a> | ||
164 | <br /> | ||
165 | <br /> | ||
166 | </p> | ||
167 | <p>{intl.formatMessage(messages.textVolunteers)}</p> | ||
168 | <p> | ||
169 | {intl.formatMessage(messages.textSupportWelcome)} | ||
170 | <a | ||
171 | href="https://help.getferdi.com/general/support" | ||
172 | target="_blank" | ||
173 | className="link" | ||
174 | rel="noreferrer" | ||
175 | > | ||
176 | {' '} | ||
177 | {intl.formatMessage(messages.textSupportWelcomeHere)} | ||
178 | <i className="mdi mdi-open-in-new" /> | ||
179 | </a> | ||
180 | </p> | ||
181 | <p> | ||
182 | {intl.formatMessage(messages.textExpenses)} | ||
183 | <a | ||
184 | href="https://opencollective.com/getferdi#section-budget" | ||
185 | target="_blank" | ||
186 | className="link" | ||
187 | rel="noreferrer" | ||
188 | > | ||
189 | {' '} | ||
190 | {intl.formatMessage(messages.textOpenCollective)} | ||
191 | <i className="mdi mdi-open-in-new" /> | ||
192 | </a> | ||
193 | </p> | ||
194 | <p> | ||
195 | {intl.formatMessage(messages.textDonation)} | ||
196 | <a | ||
197 | href="https://opencollective.com/getferdi#section-contribute" | ||
198 | target="_blank" | ||
199 | className="link" | ||
200 | rel="noreferrer" | ||
201 | > | ||
202 | {' '} | ||
203 | {intl.formatMessage(messages.textOpenCollective)} | ||
204 | <i className="mdi mdi-open-in-new" /> | ||
205 | </a>{' '} | ||
206 | {intl.formatMessage(messages.textDonationAnd)} | ||
207 | <a | ||
208 | href="https://github.com/sponsors/getferdi" | ||
209 | target="_blank" | ||
210 | className="link" | ||
211 | rel="noreferrer" | ||
212 | > | ||
213 | {' '} | ||
214 | {intl.formatMessage(messages.textGitHubSponsors)} | ||
215 | <i className="mdi mdi-open-in-new" /> | ||
216 | </a> | ||
217 | </p> | ||
218 | </div> | ||
219 | </div> | ||
220 | <InfoBar | ||
221 | sticky | ||
222 | type="primary" | ||
223 | ctaLabel={intl.formatMessage(messages.openSurvey)} | ||
224 | onClick={openSurveyWindow} | ||
225 | > | ||
226 | {intl.formatMessage(messages.bannerText)} | ||
227 | </InfoBar> | ||
228 | </div> | ||
229 | ); | ||
230 | }; | ||
231 | |||
232 | export default SupportFerdiDashboard; | ||
diff --git a/src/components/settings/team/TeamDashboard.js b/src/components/settings/team/TeamDashboard.js index 06f244997..4a1a02571 100644 --- a/src/components/settings/team/TeamDashboard.js +++ b/src/components/settings/team/TeamDashboard.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/settings/user/EditUserForm.js b/src/components/settings/user/EditUserForm.js index adc107ccc..1b8a4f25a 100644 --- a/src/components/settings/user/EditUserForm.js +++ b/src/components/settings/user/EditUserForm.js | |||
@@ -1,12 +1,11 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | import { Input } from '@meetfranz/forms'; | ||
7 | 6 | ||
7 | import { Input } from '../../ui/input/index'; | ||
8 | import Form from '../../../lib/Form'; | 8 | import Form from '../../../lib/Form'; |
9 | // import Input from '../../ui/Input'; | ||
10 | import Button from '../../ui/Button'; | 9 | import Button from '../../ui/Button'; |
11 | import Radio from '../../ui/Radio'; | 10 | import Radio from '../../ui/Radio'; |
12 | import Infobox from '../../ui/Infobox'; | 11 | import Infobox from '../../ui/Infobox'; |
diff --git a/src/components/ui/AppLoader/index.js b/src/components/ui/AppLoader/index.tsx index fa4a719ab..c7c290a57 100644 --- a/src/components/ui/AppLoader/index.js +++ b/src/components/ui/AppLoader/index.tsx | |||
@@ -1,5 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import injectSheet, { withTheme } from 'react-jss'; | 2 | import injectSheet, { withTheme } from 'react-jss'; |
4 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
5 | 4 | ||
@@ -19,15 +18,15 @@ const textList = shuffleArray([ | |||
19 | 'Fixing bugs', | 18 | 'Fixing bugs', |
20 | ]); | 19 | ]); |
21 | 20 | ||
21 | type Props = { | ||
22 | classes: typeof styles; | ||
23 | theme: any; | ||
24 | texts: string[]; | ||
25 | }; | ||
26 | |||
22 | @injectSheet(styles) | 27 | @injectSheet(styles) |
23 | @withTheme | 28 | @withTheme |
24 | class AppLoader extends Component { | 29 | class AppLoader extends Component<Props> { |
25 | static propTypes = { | ||
26 | classes: PropTypes.object.isRequired, | ||
27 | theme: PropTypes.object.isRequired, | ||
28 | texts: PropTypes.array, | ||
29 | }; | ||
30 | |||
31 | static defaultProps = { | 30 | static defaultProps = { |
32 | texts: textList, | 31 | texts: textList, |
33 | }; | 32 | }; |
@@ -36,18 +35,20 @@ class AppLoader extends Component { | |||
36 | step: 0, | 35 | step: 0, |
37 | }; | 36 | }; |
38 | 37 | ||
39 | interval = null; | 38 | interval: NodeJS.Timeout | null = null; |
40 | 39 | ||
41 | componentDidMount() { | 40 | componentDidMount() { |
42 | this.interval = setInterval(() => { | 41 | this.interval = setInterval(() => { |
43 | this.setState(prevState => ({ | 42 | this.setState((prevState: { step: number }) => ({ |
44 | step: prevState.step === textList.length - 1 ? 0 : prevState.step + 1, | 43 | step: prevState.step === textList.length - 1 ? 0 : prevState.step + 1, |
45 | })); | 44 | })); |
46 | }, 2500); | 45 | }, 2500); |
47 | } | 46 | } |
48 | 47 | ||
49 | componentWillUnmount() { | 48 | componentWillUnmount() { |
50 | clearInterval(this.interval); | 49 | if (this.interval) { |
50 | clearInterval(this.interval); | ||
51 | } | ||
51 | } | 52 | } |
52 | 53 | ||
53 | render() { | 54 | render() { |
diff --git a/src/components/ui/AppLoader/styles.js b/src/components/ui/AppLoader/styles.ts index 9891e0387..9891e0387 100644 --- a/src/components/ui/AppLoader/styles.js +++ b/src/components/ui/AppLoader/styles.ts | |||
diff --git a/src/components/ui/Button.js b/src/components/ui/Button.js deleted file mode 100644 index f6c9fd3d3..000000000 --- a/src/components/ui/Button.js +++ /dev/null | |||
@@ -1,95 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer, inject } from 'mobx-react'; | ||
4 | import Loader from 'react-loader'; | ||
5 | import classnames from 'classnames'; | ||
6 | |||
7 | @inject('stores') | ||
8 | @observer | ||
9 | class Button extends Component { | ||
10 | static propTypes = { | ||
11 | className: PropTypes.string, | ||
12 | label: PropTypes.string.isRequired, | ||
13 | disabled: PropTypes.bool, | ||
14 | onClick: PropTypes.func, | ||
15 | type: PropTypes.string, | ||
16 | buttonType: PropTypes.string, | ||
17 | loaded: PropTypes.bool, | ||
18 | htmlForm: PropTypes.string, | ||
19 | stores: PropTypes.shape({ | ||
20 | settings: PropTypes.shape({ | ||
21 | app: PropTypes.shape({ | ||
22 | accentColor: PropTypes.string.isRequired, | ||
23 | }).isRequired, | ||
24 | }).isRequired, | ||
25 | }).isRequired, | ||
26 | }; | ||
27 | |||
28 | static defaultProps = { | ||
29 | className: null, | ||
30 | disabled: false, | ||
31 | onClick: () => {}, | ||
32 | type: 'button', | ||
33 | buttonType: '', | ||
34 | loaded: true, | ||
35 | htmlForm: '', | ||
36 | }; | ||
37 | |||
38 | element = null; | ||
39 | |||
40 | render() { | ||
41 | const { | ||
42 | label, | ||
43 | className, | ||
44 | disabled, | ||
45 | onClick, | ||
46 | type, | ||
47 | buttonType, | ||
48 | loaded, | ||
49 | htmlForm, | ||
50 | } = this.props; | ||
51 | |||
52 | const buttonProps = { | ||
53 | className: classnames({ | ||
54 | 'franz-form__button': true, | ||
55 | [`franz-form__button--${buttonType}`]: buttonType, | ||
56 | [`${className}`]: className, | ||
57 | }), | ||
58 | type, | ||
59 | }; | ||
60 | |||
61 | if (disabled) { | ||
62 | buttonProps.disabled = true; | ||
63 | } | ||
64 | |||
65 | if (onClick) { | ||
66 | buttonProps.onClick = onClick; | ||
67 | } | ||
68 | |||
69 | if (htmlForm) { | ||
70 | buttonProps.form = htmlForm; | ||
71 | } | ||
72 | |||
73 | return ( | ||
74 | // disabling rule as button has type defined in `buttonProps` | ||
75 | /* eslint-disable react/button-has-type */ | ||
76 | <button {...buttonProps}> | ||
77 | <Loader | ||
78 | loaded={loaded} | ||
79 | lines={10} | ||
80 | scale={0.4} | ||
81 | color={ | ||
82 | buttonType !== 'secondary' | ||
83 | ? '#FFF' | ||
84 | : this.props.stores.settings.app.accentColor | ||
85 | } | ||
86 | component="span" | ||
87 | /> | ||
88 | {label} | ||
89 | </button> | ||
90 | /* eslint-enable react/button-has-type */ | ||
91 | ); | ||
92 | } | ||
93 | } | ||
94 | |||
95 | export default Button; | ||
diff --git a/src/components/ui/Button.tsx b/src/components/ui/Button.tsx new file mode 100644 index 000000000..aac080fda --- /dev/null +++ b/src/components/ui/Button.tsx | |||
@@ -0,0 +1,73 @@ | |||
1 | import { Component } from 'react'; | ||
2 | import { observer, inject } from 'mobx-react'; | ||
3 | import Loader from 'react-loader'; | ||
4 | import classnames from 'classnames'; | ||
5 | import { FerdiStores } from '../../stores.types'; | ||
6 | |||
7 | type Props = { | ||
8 | className: string; | ||
9 | label: string; | ||
10 | disabled: boolean; | ||
11 | onClick: () => void; | ||
12 | type: 'button' | 'submit' | 'reset'; | ||
13 | buttonType: string; | ||
14 | loaded: boolean; | ||
15 | htmlForm: string; | ||
16 | stores: FerdiStores; | ||
17 | }; | ||
18 | |||
19 | @inject('stores') | ||
20 | @observer | ||
21 | class Button extends Component<Props> { | ||
22 | static defaultProps = { | ||
23 | className: null, | ||
24 | disabled: false, | ||
25 | onClick: () => {}, | ||
26 | type: 'button', | ||
27 | buttonType: '', | ||
28 | loaded: true, | ||
29 | htmlForm: '', | ||
30 | }; | ||
31 | |||
32 | render() { | ||
33 | const { | ||
34 | label, | ||
35 | className, | ||
36 | disabled, | ||
37 | onClick, | ||
38 | type, | ||
39 | buttonType, | ||
40 | loaded, | ||
41 | htmlForm, | ||
42 | } = this.props; | ||
43 | |||
44 | return ( | ||
45 | <button | ||
46 | className={classnames({ | ||
47 | 'franz-form__button': true, | ||
48 | [`franz-form__button--${buttonType}`]: buttonType, | ||
49 | [`${className}`]: className, | ||
50 | })} | ||
51 | disabled={disabled} | ||
52 | type={type} | ||
53 | onClick={onClick} | ||
54 | form={htmlForm} | ||
55 | > | ||
56 | <Loader | ||
57 | loaded={loaded} | ||
58 | lines={10} | ||
59 | scale={0.4} | ||
60 | color={ | ||
61 | buttonType !== 'secondary' | ||
62 | ? '#FFF' | ||
63 | : this.props.stores.settings.app.accentColor | ||
64 | } | ||
65 | component="span" | ||
66 | /> | ||
67 | {label} | ||
68 | </button> | ||
69 | ); | ||
70 | } | ||
71 | } | ||
72 | |||
73 | export default Button; | ||
diff --git a/src/components/ui/FAB.js b/src/components/ui/FAB.js deleted file mode 100644 index a3aa06bc9..000000000 --- a/src/components/ui/FAB.js +++ /dev/null | |||
@@ -1,65 +0,0 @@ | |||
1 | /** | ||
2 | * Floating Action Button (FAB) | ||
3 | */ | ||
4 | import React, { Component } from 'react'; | ||
5 | import PropTypes from 'prop-types'; | ||
6 | import { observer } from 'mobx-react'; | ||
7 | import classnames from 'classnames'; | ||
8 | |||
9 | import { oneOrManyChildElements } from '../../prop-types'; | ||
10 | |||
11 | @observer | ||
12 | class Button extends Component { | ||
13 | static propTypes = { | ||
14 | className: PropTypes.string, | ||
15 | disabled: PropTypes.bool, | ||
16 | onClick: PropTypes.func, | ||
17 | type: PropTypes.string, | ||
18 | children: oneOrManyChildElements.isRequired, | ||
19 | htmlForm: PropTypes.string, | ||
20 | }; | ||
21 | |||
22 | static defaultProps = { | ||
23 | className: null, | ||
24 | disabled: false, | ||
25 | onClick: () => {}, | ||
26 | type: 'button', | ||
27 | htmlForm: '', | ||
28 | }; | ||
29 | |||
30 | element = null; | ||
31 | |||
32 | render() { | ||
33 | const { className, disabled, onClick, type, children, htmlForm } = | ||
34 | this.props; | ||
35 | |||
36 | const buttonProps = { | ||
37 | className: classnames({ | ||
38 | ferdi__fab: true, | ||
39 | [`${className}`]: className, | ||
40 | }), | ||
41 | type, | ||
42 | }; | ||
43 | |||
44 | if (disabled) { | ||
45 | buttonProps.disabled = true; | ||
46 | } | ||
47 | |||
48 | if (onClick) { | ||
49 | buttonProps.onClick = onClick; | ||
50 | } | ||
51 | |||
52 | if (htmlForm) { | ||
53 | buttonProps.form = htmlForm; | ||
54 | } | ||
55 | |||
56 | return ( | ||
57 | // disabling rule as button has type defined in `buttonProps` | ||
58 | <button {...buttonProps} type="button"> | ||
59 | {children} | ||
60 | </button> | ||
61 | ); | ||
62 | } | ||
63 | } | ||
64 | |||
65 | export default Button; | ||
diff --git a/src/components/ui/FAB.tsx b/src/components/ui/FAB.tsx new file mode 100644 index 000000000..583c9d556 --- /dev/null +++ b/src/components/ui/FAB.tsx | |||
@@ -0,0 +1,51 @@ | |||
1 | /** | ||
2 | * Floating Action Button (FAB) | ||
3 | */ | ||
4 | import { Component, ReactChildren } from 'react'; | ||
5 | import { observer } from 'mobx-react'; | ||
6 | import classnames from 'classnames'; | ||
7 | |||
8 | type Props = { | ||
9 | className: string; | ||
10 | disabled: boolean; | ||
11 | onClick: () => void; | ||
12 | type: string; | ||
13 | children: ReactChildren; | ||
14 | htmlForm: string; | ||
15 | }; | ||
16 | |||
17 | @observer | ||
18 | class Button extends Component<Props> { | ||
19 | static defaultProps = { | ||
20 | disabled: false, | ||
21 | onClick: () => {}, | ||
22 | type: 'button', | ||
23 | htmlForm: '', | ||
24 | }; | ||
25 | |||
26 | element = null; | ||
27 | |||
28 | render() { | ||
29 | const { className, disabled, onClick, type, children, htmlForm } = | ||
30 | this.props; | ||
31 | |||
32 | const buttonProps = { | ||
33 | className: classnames({ | ||
34 | ferdi__fab: true, | ||
35 | [`${className}`]: className, | ||
36 | }), | ||
37 | type, | ||
38 | disabled, | ||
39 | onClick, | ||
40 | form: htmlForm, | ||
41 | }; | ||
42 | |||
43 | return ( | ||
44 | <button {...buttonProps} type="button"> | ||
45 | {children} | ||
46 | </button> | ||
47 | ); | ||
48 | } | ||
49 | } | ||
50 | |||
51 | export default Button; | ||
diff --git a/src/components/ui/FullscreenLoader/index.js b/src/components/ui/FullscreenLoader/index.js index ab5e2f365..f5943f3f3 100644 --- a/src/components/ui/FullscreenLoader/index.js +++ b/src/components/ui/FullscreenLoader/index.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet, { withTheme } from 'react-jss'; | 4 | import injectSheet, { withTheme } from 'react-jss'; |
diff --git a/src/components/ui/FullscreenLoader/styles.js b/src/components/ui/FullscreenLoader/styles.ts index 64d24e4ce..64d24e4ce 100644 --- a/src/components/ui/FullscreenLoader/styles.js +++ b/src/components/ui/FullscreenLoader/styles.ts | |||
diff --git a/src/components/ui/ImageUpload.js b/src/components/ui/ImageUpload.tsx index 49aff389b..4b25be502 100644 --- a/src/components/ui/ImageUpload.js +++ b/src/components/ui/ImageUpload.tsx | |||
@@ -1,23 +1,21 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import { Field } from 'mobx-react-form'; | 3 | import { Field } from 'mobx-react-form'; |
5 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
6 | import Dropzone from 'react-dropzone'; | 5 | import Dropzone, { DropzoneRef } from 'react-dropzone'; |
7 | import { isWindows } from '../../environment'; | 6 | import { isWindows } from '../../environment'; |
8 | 7 | ||
9 | @observer | 8 | type Props = { |
10 | class ImageUpload extends Component { | 9 | field: typeof Field; |
11 | static propTypes = { | 10 | className: string; |
12 | field: PropTypes.instanceOf(Field).isRequired, | 11 | multiple: boolean; |
13 | className: PropTypes.string, | 12 | textDelete: string; |
14 | multiple: PropTypes.bool, | 13 | textUpload: string; |
15 | textDelete: PropTypes.string.isRequired, | 14 | }; |
16 | textUpload: PropTypes.string.isRequired, | ||
17 | }; | ||
18 | 15 | ||
16 | @observer | ||
17 | class ImageUpload extends Component<Props> { | ||
19 | static defaultProps = { | 18 | static defaultProps = { |
20 | className: null, | ||
21 | multiple: false, | 19 | multiple: false, |
22 | }; | 20 | }; |
23 | 21 | ||
@@ -25,7 +23,7 @@ class ImageUpload extends Component { | |||
25 | path: null, | 23 | path: null, |
26 | }; | 24 | }; |
27 | 25 | ||
28 | dropzoneRef = null; | 26 | dropzoneRef: DropzoneRef | null = null; |
29 | 27 | ||
30 | onDrop(acceptedFiles) { | 28 | onDrop(acceptedFiles) { |
31 | const { field } = this.props; | 29 | const { field } = this.props; |
diff --git a/src/components/ui/InfoBar.js b/src/components/ui/InfoBar.js index dc6be10da..3311a949f 100644 --- a/src/components/ui/InfoBar.js +++ b/src/components/ui/InfoBar.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
diff --git a/src/components/ui/Infobox.js b/src/components/ui/Infobox.js index 9e34bf110..b88b01bd8 100644 --- a/src/components/ui/Infobox.js +++ b/src/components/ui/Infobox.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
diff --git a/src/components/ui/Input.js b/src/components/ui/Input.js index 43fab10ee..8d37d7a05 100644 --- a/src/components/ui/Input.js +++ b/src/components/ui/Input.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { Field } from 'mobx-react-form'; | 4 | import { Field } from 'mobx-react-form'; |
diff --git a/src/components/ui/Link.js b/src/components/ui/Link.js index 94db3f842..40766c984 100644 --- a/src/components/ui/Link.js +++ b/src/components/ui/Link.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { RouterStore } from 'mobx-react-router'; | 4 | import { RouterStore } from 'mobx-react-router'; |
diff --git a/src/components/ui/Loader.js b/src/components/ui/Loader.tsx index 46c1390bf..1173c11e7 100644 --- a/src/components/ui/Loader.js +++ b/src/components/ui/Loader.tsx | |||
@@ -1,31 +1,22 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component, ReactChildren } from 'react'; |
2 | import { observer, inject } from 'mobx-react'; | 2 | import { observer, inject } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | ||
4 | import Loader from 'react-loader'; | 3 | import Loader from 'react-loader'; |
5 | 4 | ||
6 | import { oneOrManyChildElements } from '../../prop-types'; | 5 | import { FerdiStores } from '../../stores.types'; |
6 | |||
7 | type Props = { | ||
8 | children: ReactChildren; | ||
9 | loaded: boolean; | ||
10 | className: string; | ||
11 | color: string; | ||
12 | stores: FerdiStores; | ||
13 | }; | ||
7 | 14 | ||
8 | @inject('stores') | 15 | @inject('stores') |
9 | @observer | 16 | @observer |
10 | class LoaderComponent extends Component { | 17 | class LoaderComponent extends Component<Props> { |
11 | static propTypes = { | ||
12 | children: oneOrManyChildElements, | ||
13 | loaded: PropTypes.bool, | ||
14 | className: PropTypes.string, | ||
15 | color: PropTypes.string, | ||
16 | stores: PropTypes.shape({ | ||
17 | settings: PropTypes.shape({ | ||
18 | app: PropTypes.shape({ | ||
19 | accentColor: PropTypes.string.isRequired, | ||
20 | }).isRequired, | ||
21 | }).isRequired, | ||
22 | }).isRequired, | ||
23 | }; | ||
24 | |||
25 | static defaultProps = { | 18 | static defaultProps = { |
26 | children: null, | ||
27 | loaded: false, | 19 | loaded: false, |
28 | className: '', | ||
29 | color: 'ACCENT', | 20 | color: 'ACCENT', |
30 | }; | 21 | }; |
31 | 22 | ||
@@ -40,7 +31,6 @@ class LoaderComponent extends Component { | |||
40 | return ( | 31 | return ( |
41 | <Loader | 32 | <Loader |
42 | loaded={loaded} | 33 | loaded={loaded} |
43 | // lines={10} | ||
44 | width={4} | 34 | width={4} |
45 | scale={0.6} | 35 | scale={0.6} |
46 | color={color} | 36 | color={color} |
diff --git a/src/components/ui/Modal/index.js b/src/components/ui/Modal/index.tsx index 3c7c66c59..f2f4461b8 100644 --- a/src/components/ui/Modal/index.js +++ b/src/components/ui/Modal/index.tsx | |||
@@ -1,28 +1,25 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component, ReactChildren } from 'react'; |
2 | import ReactModal from 'react-modal'; | 2 | import ReactModal from 'react-modal'; |
3 | import PropTypes from 'prop-types'; | ||
4 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
5 | import injectCSS from 'react-jss'; | 4 | import injectCSS from 'react-jss'; |
6 | import { Icon } from '@meetfranz/ui'; | ||
7 | |||
8 | import { mdiClose } from '@mdi/js'; | 5 | import { mdiClose } from '@mdi/js'; |
6 | |||
7 | import { Icon } from '../icon'; | ||
9 | import styles from './styles'; | 8 | import styles from './styles'; |
10 | 9 | ||
11 | // ReactModal.setAppElement('#root'); | 10 | type Props = { |
11 | children: ReactChildren; | ||
12 | className: string; | ||
13 | classes: any; | ||
14 | isOpen: boolean; | ||
15 | portal: string; | ||
16 | close: () => void; | ||
17 | shouldCloseOnOverlayClick: boolean; | ||
18 | showClose: boolean; | ||
19 | }; | ||
12 | 20 | ||
13 | @injectCSS(styles) | 21 | @injectCSS(styles) |
14 | class Modal extends Component { | 22 | class Modal extends Component<Props> { |
15 | static propTypes = { | ||
16 | children: PropTypes.node.isRequired, | ||
17 | className: PropTypes.string, | ||
18 | classes: PropTypes.object.isRequired, | ||
19 | isOpen: PropTypes.bool.isRequired, | ||
20 | portal: PropTypes.string, | ||
21 | close: PropTypes.func.isRequired, | ||
22 | shouldCloseOnOverlayClick: PropTypes.bool, | ||
23 | showClose: PropTypes.bool, | ||
24 | }; | ||
25 | |||
26 | static defaultProps = { | 23 | static defaultProps = { |
27 | className: null, | 24 | className: null, |
28 | portal: 'modal-portal', | 25 | portal: 'modal-portal', |
diff --git a/src/components/ui/Modal/styles.js b/src/components/ui/Modal/styles.ts index f32c075ce..c2bebf9bb 100644 --- a/src/components/ui/Modal/styles.js +++ b/src/components/ui/Modal/styles.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | export default (theme) => ({ | 1 | export default theme => ({ |
2 | component: { | 2 | component: { |
3 | zIndex: 500, | 3 | zIndex: 500, |
4 | position: 'absolute', | 4 | position: 'absolute', |
diff --git a/src/components/ui/Radio.js b/src/components/ui/Radio.tsx index 65a777ff1..594ea70e4 100644 --- a/src/components/ui/Radio.js +++ b/src/components/ui/Radio.tsx | |||
@@ -1,20 +1,18 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import { Field } from 'mobx-react-form'; | 3 | import { Field } from 'mobx-react-form'; |
5 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
6 | 5 | ||
7 | @observer | 6 | type Props = { |
8 | class Radio extends Component { | 7 | field: typeof Field; |
9 | static propTypes = { | 8 | className: string; |
10 | field: PropTypes.instanceOf(Field).isRequired, | 9 | focus: boolean; |
11 | className: PropTypes.string, | 10 | showLabel: boolean; |
12 | focus: PropTypes.bool, | 11 | }; |
13 | showLabel: PropTypes.bool, | ||
14 | }; | ||
15 | 12 | ||
13 | @observer | ||
14 | class Radio extends Component<Props> { | ||
16 | static defaultProps = { | 15 | static defaultProps = { |
17 | className: null, | ||
18 | focus: false, | 16 | focus: false, |
19 | showLabel: true, | 17 | showLabel: true, |
20 | }; | 18 | }; |
@@ -28,6 +26,7 @@ class Radio extends Component { | |||
28 | } | 26 | } |
29 | 27 | ||
30 | focus() { | 28 | focus() { |
29 | // @ts-expect-error Object is possibly 'null'. | ||
31 | this.inputElement.focus(); | 30 | this.inputElement.focus(); |
32 | } | 31 | } |
33 | 32 | ||
diff --git a/src/components/ui/SearchInput.js b/src/components/ui/SearchInput.tsx index 2d760beab..af55b0e11 100644 --- a/src/components/ui/SearchInput.js +++ b/src/components/ui/SearchInput.tsx | |||
@@ -1,23 +1,22 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { ChangeEvent, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 3 | import classnames from 'classnames'; |
5 | import { debounce } from 'lodash'; | 4 | import { debounce } from 'lodash'; |
6 | 5 | ||
7 | @observer | 6 | type Props = { |
8 | class SearchInput extends Component { | 7 | value: string; |
9 | static propTypes = { | 8 | placeholder: string; |
10 | value: PropTypes.string, | 9 | className: string; |
11 | placeholder: PropTypes.string, | 10 | onChange: (e: ChangeEvent<HTMLInputElement>) => void; |
12 | className: PropTypes.string, | 11 | onReset: () => void; |
13 | onChange: PropTypes.func, | 12 | name: string; |
14 | onReset: PropTypes.func, | 13 | throttle: boolean; |
15 | name: PropTypes.string, | 14 | throttleDelay: number; |
16 | throttle: PropTypes.bool, | 15 | autoFocus: boolean; |
17 | throttleDelay: PropTypes.number, | 16 | }; |
18 | autoFocus: PropTypes.bool, | ||
19 | }; | ||
20 | 17 | ||
18 | @observer | ||
19 | class SearchInput extends Component<Props> { | ||
21 | static defaultProps = { | 20 | static defaultProps = { |
22 | value: '', | 21 | value: '', |
23 | placeholder: '', | 22 | placeholder: '', |
@@ -32,7 +31,7 @@ class SearchInput extends Component { | |||
32 | 31 | ||
33 | input = null; | 32 | input = null; |
34 | 33 | ||
35 | constructor(props) { | 34 | constructor(props: Props) { |
36 | super(props); | 35 | super(props); |
37 | 36 | ||
38 | this.state = { | 37 | this.state = { |
@@ -49,24 +48,27 @@ class SearchInput extends Component { | |||
49 | const { autoFocus } = this.props; | 48 | const { autoFocus } = this.props; |
50 | 49 | ||
51 | if (autoFocus) { | 50 | if (autoFocus) { |
51 | // @ts-expect-error Object is possibly 'null'. | ||
52 | this.input.focus(); | 52 | this.input.focus(); |
53 | } | 53 | } |
54 | } | 54 | } |
55 | 55 | ||
56 | onChange(e) { | 56 | onChange(e: ChangeEvent<HTMLInputElement>) { |
57 | const { throttle, onChange } = this.props; | 57 | const { throttle, onChange } = this.props; |
58 | const { value } = e.target; | 58 | const { value } = e.target; |
59 | this.setState({ value }); | 59 | this.setState({ value }); |
60 | 60 | ||
61 | if (throttle) { | 61 | if (throttle) { |
62 | e.persist(); | 62 | e.persist(); |
63 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
63 | this.throttledOnChange(value); | 64 | this.throttledOnChange(value); |
64 | } else { | 65 | } else { |
66 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'ChangeEvent<HTMLInputElement>'. | ||
65 | onChange(value); | 67 | onChange(value); |
66 | } | 68 | } |
67 | } | 69 | } |
68 | 70 | ||
69 | throttledOnChange(e) { | 71 | throttledOnChange(e: ChangeEvent<HTMLInputElement>) { |
70 | const { onChange } = this.props; | 72 | const { onChange } = this.props; |
71 | 73 | ||
72 | onChange(e); | 74 | onChange(e); |
@@ -81,6 +83,7 @@ class SearchInput extends Component { | |||
81 | 83 | ||
82 | render() { | 84 | render() { |
83 | const { className, name, placeholder } = this.props; | 85 | const { className, name, placeholder } = this.props; |
86 | // @ts-expect-error Property 'value' does not exist on type 'Readonly<{}>'. | ||
84 | const { value } = this.state; | 87 | const { value } = this.state; |
85 | 88 | ||
86 | return ( | 89 | return ( |
@@ -94,6 +97,7 @@ class SearchInput extends Component { | |||
94 | value={value} | 97 | value={value} |
95 | onChange={e => this.onChange(e)} | 98 | onChange={e => this.onChange(e)} |
96 | ref={ref => { | 99 | ref={ref => { |
100 | // @ts-expect-error Type 'HTMLInputElement | null' is not assignable to type 'null'. | ||
97 | this.input = ref; | 101 | this.input = ref; |
98 | }} | 102 | }} |
99 | /> | 103 | /> |
diff --git a/src/components/ui/Select.js b/src/components/ui/Select.js index 5ac7ddd6d..a560da332 100644 --- a/src/components/ui/Select.js +++ b/src/components/ui/Select.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { createRef, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { Field } from 'mobx-react-form'; | 4 | import { Field } from 'mobx-react-form'; |
@@ -24,7 +24,7 @@ class Select extends Component { | |||
24 | constructor(props) { | 24 | constructor(props) { |
25 | super(props); | 25 | super(props); |
26 | 26 | ||
27 | this.element = React.createRef(); | 27 | this.element = createRef(); |
28 | } | 28 | } |
29 | 29 | ||
30 | multipleChange() { | 30 | multipleChange() { |
diff --git a/src/components/ui/ServiceIcon.js b/src/components/ui/ServiceIcon.js index b2dadeac3..f067f8955 100644 --- a/src/components/ui/ServiceIcon.js +++ b/src/components/ui/ServiceIcon.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
@@ -6,7 +6,7 @@ import classnames from 'classnames'; | |||
6 | 6 | ||
7 | import ServiceModel from '../../models/Service'; | 7 | import ServiceModel from '../../models/Service'; |
8 | 8 | ||
9 | const styles = (theme) => ({ | 9 | const styles = theme => ({ |
10 | root: { | 10 | root: { |
11 | height: 'auto', | 11 | height: 'auto', |
12 | }, | 12 | }, |
@@ -24,7 +24,8 @@ const styles = (theme) => ({ | |||
24 | }, | 24 | }, |
25 | }); | 25 | }); |
26 | 26 | ||
27 | @injectSheet(styles) @observer | 27 | @injectSheet(styles) |
28 | @observer | ||
28 | class ServiceIcon extends Component { | 29 | class ServiceIcon extends Component { |
29 | static propTypes = { | 30 | static propTypes = { |
30 | classes: PropTypes.object.isRequired, | 31 | classes: PropTypes.object.isRequired, |
@@ -37,19 +38,10 @@ class ServiceIcon extends Component { | |||
37 | }; | 38 | }; |
38 | 39 | ||
39 | render() { | 40 | render() { |
40 | const { | 41 | const { classes, className, service } = this.props; |
41 | classes, | ||
42 | className, | ||
43 | service, | ||
44 | } = this.props; | ||
45 | 42 | ||
46 | return ( | 43 | return ( |
47 | <div | 44 | <div className={classnames([classes.root, className])}> |
48 | className={classnames([ | ||
49 | classes.root, | ||
50 | className, | ||
51 | ])} | ||
52 | > | ||
53 | <img | 45 | <img |
54 | src={service.icon} | 46 | src={service.icon} |
55 | className={classnames([ | 47 | className={classnames([ |
diff --git a/src/components/ui/Slider.js b/src/components/ui/Slider.js index 6f17eae00..dea6e0563 100644 --- a/src/components/ui/Slider.js +++ b/src/components/ui/Slider.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
diff --git a/src/components/ui/StatusBarTargetUrl.js b/src/components/ui/StatusBarTargetUrl.js index ff4e8c795..38b436742 100644 --- a/src/components/ui/StatusBarTargetUrl.js +++ b/src/components/ui/StatusBarTargetUrl.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
diff --git a/src/components/ui/Tabs/TabItem.tsx b/src/components/ui/Tabs/TabItem.tsx index bd613ddc7..9fcc3c41e 100644 --- a/src/components/ui/Tabs/TabItem.tsx +++ b/src/components/ui/Tabs/TabItem.tsx | |||
@@ -1,3 +1 @@ | |||
1 | import React from 'react'; | ||
2 | |||
3 | export const TabItem = ({ children }) => <>{children}</>; | export const TabItem = ({ children }) => <>{children}</>; | |
diff --git a/src/components/ui/Tabs/Tabs.js b/src/components/ui/Tabs/Tabs.js index 195398708..77803974b 100644 --- a/src/components/ui/Tabs/Tabs.js +++ b/src/components/ui/Tabs/Tabs.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Children, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
@@ -36,7 +36,7 @@ class Tab extends Component { | |||
36 | return ( | 36 | return ( |
37 | <div className="content-tabs"> | 37 | <div className="content-tabs"> |
38 | <div className="content-tabs__tabs"> | 38 | <div className="content-tabs__tabs"> |
39 | {React.Children.map(children, (child, i) => ( | 39 | {Children.map(children, (child, i) => ( |
40 | <button | 40 | <button |
41 | key="{i}" | 41 | key="{i}" |
42 | className={classnames({ | 42 | className={classnames({ |
@@ -51,7 +51,7 @@ class Tab extends Component { | |||
51 | ))} | 51 | ))} |
52 | </div> | 52 | </div> |
53 | <div className="content-tabs__content"> | 53 | <div className="content-tabs__content"> |
54 | {React.Children.map(children, (child, i) => ( | 54 | {Children.map(children, (child, i) => ( |
55 | <div | 55 | <div |
56 | key="{i}" | 56 | key="{i}" |
57 | className={classnames({ | 57 | className={classnames({ |
diff --git a/src/components/ui/Toggle.js b/src/components/ui/Toggle.js index bd7bc242d..dfc319735 100644 --- a/src/components/ui/Toggle.js +++ b/src/components/ui/Toggle.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
diff --git a/src/components/ui/ToggleRaw.js b/src/components/ui/ToggleRaw.js index 1fde879ac..74292a870 100644 --- a/src/components/ui/ToggleRaw.js +++ b/src/components/ui/ToggleRaw.js | |||
@@ -1,7 +1,7 @@ | |||
1 | /** | 1 | /** |
2 | * "Raw" Toggle - for usage without a MobX Form element | 2 | * "Raw" Toggle - for usage without a MobX Form element |
3 | */ | 3 | */ |
4 | import React, { Component } from 'react'; | 4 | import { Component } from 'react'; |
5 | import PropTypes from 'prop-types'; | 5 | import PropTypes from 'prop-types'; |
6 | import { observer } from 'mobx-react'; | 6 | import { observer } from 'mobx-react'; |
7 | import classnames from 'classnames'; | 7 | import classnames from 'classnames'; |
diff --git a/src/components/ui/WebviewLoader/index.js b/src/components/ui/WebviewLoader/index.js index 8f4499e7b..8d4513172 100644 --- a/src/components/ui/WebviewLoader/index.js +++ b/src/components/ui/WebviewLoader/index.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
diff --git a/src/components/ui/WebviewLoader/styles.js b/src/components/ui/WebviewLoader/styles.ts index 5d58011fe..dbd75db8a 100644 --- a/src/components/ui/WebviewLoader/styles.js +++ b/src/components/ui/WebviewLoader/styles.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | export default (theme) => ({ | 1 | export default theme => ({ |
2 | component: { | 2 | component: { |
3 | background: theme.colorWebviewLoaderBackground, | 3 | background: theme.colorWebviewLoaderBackground, |
4 | padding: 20, | 4 | padding: 20, |
diff --git a/src/components/ui/badge/ProBadge.tsx b/src/components/ui/badge/ProBadge.tsx new file mode 100644 index 000000000..dc1e76f7f --- /dev/null +++ b/src/components/ui/badge/ProBadge.tsx | |||
@@ -0,0 +1,64 @@ | |||
1 | import { mdiStar } from '@mdi/js'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '../../../themes'; | ||
7 | import { Icon } from '../icon'; | ||
8 | import { Badge } from './index'; | ||
9 | import { IWithStyle } from '../typings/generic'; | ||
10 | |||
11 | interface IProps extends IWithStyle { | ||
12 | badgeClasses?: string; | ||
13 | iconClasses?: string; | ||
14 | inverted?: boolean; | ||
15 | className?: string; | ||
16 | } | ||
17 | |||
18 | const styles = (theme: Theme) => ({ | ||
19 | badge: { | ||
20 | height: 'auto', | ||
21 | padding: [4, 6, 2, 7], | ||
22 | borderRadius: theme.borderRadiusSmall, | ||
23 | }, | ||
24 | invertedBadge: { | ||
25 | background: theme.styleTypes.primary.contrast, | ||
26 | color: theme.styleTypes.primary.accent, | ||
27 | }, | ||
28 | icon: { | ||
29 | fill: theme.styleTypes.primary.contrast, | ||
30 | }, | ||
31 | invertedIcon: { | ||
32 | fill: theme.styleTypes.primary.accent, | ||
33 | }, | ||
34 | }); | ||
35 | |||
36 | class ProBadgeComponent extends Component<IProps> { | ||
37 | render() { | ||
38 | const { classes, badgeClasses, iconClasses, inverted, className } = | ||
39 | this.props; | ||
40 | |||
41 | return ( | ||
42 | <Badge | ||
43 | type="primary" | ||
44 | className={classnames([ | ||
45 | classes.badge, | ||
46 | inverted && classes.invertedBadge, | ||
47 | badgeClasses, | ||
48 | className, | ||
49 | ])} | ||
50 | > | ||
51 | <Icon | ||
52 | icon={mdiStar} | ||
53 | className={classnames([ | ||
54 | classes.icon, | ||
55 | inverted && classes.invertedIcon, | ||
56 | iconClasses, | ||
57 | ])} | ||
58 | /> | ||
59 | </Badge> | ||
60 | ); | ||
61 | } | ||
62 | } | ||
63 | |||
64 | export const ProBadge = injectStyle(styles)(ProBadgeComponent); | ||
diff --git a/src/components/ui/badge/index.tsx b/src/components/ui/badge/index.tsx new file mode 100644 index 000000000..61bede937 --- /dev/null +++ b/src/components/ui/badge/index.tsx | |||
@@ -0,0 +1,71 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, ReactNode } from 'react'; | ||
3 | import injectStyle from 'react-jss'; | ||
4 | |||
5 | import { Theme } from '../../../themes'; | ||
6 | import { IWithStyle } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | type: string; | ||
10 | className?: string; | ||
11 | children: ReactNode; | ||
12 | } | ||
13 | |||
14 | const badgeStyles = (theme: Theme) => { | ||
15 | const styles = {}; | ||
16 | Object.keys(theme.styleTypes).map(style => { | ||
17 | Object.assign(styles, { | ||
18 | [style]: { | ||
19 | background: theme.styleTypes[style].accent, | ||
20 | color: theme.styleTypes[style].contrast, | ||
21 | border: theme.styleTypes[style].border, | ||
22 | }, | ||
23 | }); | ||
24 | }); | ||
25 | |||
26 | return styles; | ||
27 | }; | ||
28 | |||
29 | const styles = (theme: Theme) => ({ | ||
30 | badge: { | ||
31 | display: 'inline-block', | ||
32 | padding: [3, 8, 4], | ||
33 | fontSize: theme.badgeFontSize, | ||
34 | borderRadius: theme.badgeBorderRadius, | ||
35 | margin: [0, 4], | ||
36 | |||
37 | '&:first-child': { | ||
38 | marginLeft: 0, | ||
39 | }, | ||
40 | |||
41 | '&:last-child': { | ||
42 | marginRight: 0, | ||
43 | }, | ||
44 | }, | ||
45 | ...badgeStyles(theme), | ||
46 | }); | ||
47 | |||
48 | class BadgeComponent extends Component<IProps> { | ||
49 | public static defaultProps = { | ||
50 | type: 'primary', | ||
51 | }; | ||
52 | |||
53 | render() { | ||
54 | const { classes, children, type, className } = this.props; | ||
55 | |||
56 | return ( | ||
57 | <div | ||
58 | className={classnames({ | ||
59 | [classes.badge]: true, | ||
60 | [classes[type]]: true, | ||
61 | [`${className}`]: className, | ||
62 | })} | ||
63 | data-type="franz-badge" | ||
64 | > | ||
65 | {children} | ||
66 | </div> | ||
67 | ); | ||
68 | } | ||
69 | } | ||
70 | |||
71 | export const Badge = injectStyle(styles)(BadgeComponent); | ||
diff --git a/src/components/ui/button/index.tsx b/src/components/ui/button/index.tsx new file mode 100644 index 000000000..12e5e4449 --- /dev/null +++ b/src/components/ui/button/index.tsx | |||
@@ -0,0 +1,265 @@ | |||
1 | import Icon from '@mdi/react'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Property } from 'csstype'; | ||
4 | import { Component, MouseEvent } from 'react'; | ||
5 | import injectStyle, { withTheme } from 'react-jss'; | ||
6 | import Loader from 'react-loader'; | ||
7 | |||
8 | import { Theme } from '../../../themes'; | ||
9 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
10 | |||
11 | type ButtonType = | ||
12 | | 'primary' | ||
13 | | 'secondary' | ||
14 | | 'success' | ||
15 | | 'danger' | ||
16 | | 'warning' | ||
17 | | 'inverted'; | ||
18 | |||
19 | interface IProps extends IFormField, IWithStyle { | ||
20 | className?: string; | ||
21 | disabled?: boolean; | ||
22 | id?: string; | ||
23 | type?: 'button' | 'reset' | 'submit' | undefined; | ||
24 | onClick: ( | ||
25 | event: MouseEvent<HTMLButtonElement> | MouseEvent<HTMLAnchorElement>, | ||
26 | ) => void; | ||
27 | buttonType?: ButtonType; | ||
28 | stretch?: boolean; | ||
29 | loaded?: boolean; | ||
30 | busy?: boolean; | ||
31 | icon?: string; | ||
32 | href?: string; | ||
33 | target?: string; | ||
34 | } | ||
35 | |||
36 | let buttonTransition: string = 'none'; | ||
37 | let loaderContainerTransition: string = 'none'; | ||
38 | |||
39 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
40 | buttonTransition = 'background .5s, opacity 0.3s'; | ||
41 | loaderContainerTransition = 'all 0.3s'; | ||
42 | } | ||
43 | |||
44 | const styles = (theme: Theme) => ({ | ||
45 | button: { | ||
46 | borderRadius: theme.borderRadiusSmall, | ||
47 | border: 'none', | ||
48 | display: 'inline-flex', | ||
49 | position: 'relative' as Property.Position, | ||
50 | transition: buttonTransition, | ||
51 | textAlign: 'center' as Property.TextAlign, | ||
52 | outline: 'none', | ||
53 | alignItems: 'center', | ||
54 | padding: 0, | ||
55 | width: (props: IProps) => | ||
56 | (props.stretch ? '100%' : 'auto') as Property.Width<string>, | ||
57 | fontSize: theme.uiFontSize, | ||
58 | textDecoration: 'none', | ||
59 | |||
60 | '&:hover': { | ||
61 | opacity: 0.8, | ||
62 | }, | ||
63 | '&:active': { | ||
64 | opacity: 0.5, | ||
65 | transition: 'none', | ||
66 | }, | ||
67 | }, | ||
68 | label: { | ||
69 | margin: '10px 20px', | ||
70 | width: '100%', | ||
71 | display: 'flex', | ||
72 | alignItems: 'center', | ||
73 | justifyContent: 'center', | ||
74 | }, | ||
75 | primary: { | ||
76 | background: theme.buttonPrimaryBackground, | ||
77 | color: theme.buttonPrimaryTextColor, | ||
78 | |||
79 | '& svg': { | ||
80 | fill: theme.buttonPrimaryTextColor, | ||
81 | }, | ||
82 | }, | ||
83 | secondary: { | ||
84 | background: theme.buttonSecondaryBackground, | ||
85 | color: theme.buttonSecondaryTextColor, | ||
86 | |||
87 | '& svg': { | ||
88 | fill: theme.buttonSecondaryTextColor, | ||
89 | }, | ||
90 | }, | ||
91 | success: { | ||
92 | background: theme.buttonSuccessBackground, | ||
93 | color: theme.buttonSuccessTextColor, | ||
94 | |||
95 | '& svg': { | ||
96 | fill: theme.buttonSuccessTextColor, | ||
97 | }, | ||
98 | }, | ||
99 | danger: { | ||
100 | background: theme.buttonDangerBackground, | ||
101 | color: theme.buttonDangerTextColor, | ||
102 | |||
103 | '& svg': { | ||
104 | fill: theme.buttonDangerTextColor, | ||
105 | }, | ||
106 | }, | ||
107 | warning: { | ||
108 | background: theme.buttonWarningBackground, | ||
109 | color: theme.buttonWarningTextColor, | ||
110 | |||
111 | '& svg': { | ||
112 | fill: theme.buttonWarningTextColor, | ||
113 | }, | ||
114 | }, | ||
115 | inverted: { | ||
116 | background: theme.buttonInvertedBackground, | ||
117 | color: theme.buttonInvertedTextColor, | ||
118 | border: theme.buttonInvertedBorder, | ||
119 | |||
120 | '& svg': { | ||
121 | fill: theme.buttonInvertedTextColor, | ||
122 | }, | ||
123 | }, | ||
124 | disabled: { | ||
125 | opacity: theme.inputDisabledOpacity, | ||
126 | }, | ||
127 | loader: { | ||
128 | position: 'relative' as Property.Position, | ||
129 | width: 20, | ||
130 | height: 18, | ||
131 | zIndex: 9999, | ||
132 | }, | ||
133 | loaderContainer: { | ||
134 | width: (props: IProps): string => (!props.busy ? '0' : '40px'), | ||
135 | height: 20, | ||
136 | overflow: 'hidden', | ||
137 | transition: loaderContainerTransition, | ||
138 | marginLeft: (props: IProps): number => (!props.busy ? 10 : 20), | ||
139 | marginRight: (props: IProps): number => (!props.busy ? -10 : -20), | ||
140 | position: (props: IProps): Property.Position => | ||
141 | props.stretch ? 'absolute' : 'inherit', | ||
142 | }, | ||
143 | icon: { | ||
144 | margin: [1, 10, 0, -5], | ||
145 | }, | ||
146 | }); | ||
147 | |||
148 | class ButtonComponent extends Component<IProps> { | ||
149 | public static defaultProps = { | ||
150 | type: 'button', | ||
151 | disabled: false, | ||
152 | onClick: () => null, | ||
153 | buttonType: 'primary' as ButtonType, | ||
154 | stretch: false, | ||
155 | busy: false, | ||
156 | }; | ||
157 | |||
158 | state = { | ||
159 | busy: false, | ||
160 | }; | ||
161 | |||
162 | componentWillMount() { | ||
163 | this.setState({ busy: this.props.busy }); | ||
164 | } | ||
165 | |||
166 | componentWillReceiveProps(nextProps: IProps) { | ||
167 | if (nextProps.busy !== this.props.busy) { | ||
168 | if (this.props.busy) { | ||
169 | setTimeout(() => { | ||
170 | this.setState({ busy: nextProps.busy }); | ||
171 | }, 300); | ||
172 | } else { | ||
173 | this.setState({ busy: nextProps.busy }); | ||
174 | } | ||
175 | } | ||
176 | } | ||
177 | |||
178 | render() { | ||
179 | const { | ||
180 | classes, | ||
181 | className, | ||
182 | theme, | ||
183 | disabled, | ||
184 | id, | ||
185 | label, | ||
186 | type, | ||
187 | onClick, | ||
188 | buttonType, | ||
189 | loaded, | ||
190 | icon, | ||
191 | href, | ||
192 | target, | ||
193 | } = this.props; | ||
194 | |||
195 | const { busy } = this.state; | ||
196 | |||
197 | let showLoader = false; | ||
198 | if (loaded) { | ||
199 | showLoader = !loaded; | ||
200 | console.warn( | ||
201 | 'Ferdi Button prop `loaded` will be deprecated in the future. Please use `busy` instead', | ||
202 | ); | ||
203 | } | ||
204 | if (busy) { | ||
205 | showLoader = busy; | ||
206 | } | ||
207 | |||
208 | const content = ( | ||
209 | <> | ||
210 | <div className={classes.loaderContainer}> | ||
211 | {showLoader && ( | ||
212 | <Loader | ||
213 | loaded={false} | ||
214 | width={4} | ||
215 | scale={0.45} | ||
216 | color={theme.buttonLoaderColor[buttonType!]} | ||
217 | parentClassName={classes.loader} | ||
218 | /> | ||
219 | )} | ||
220 | </div> | ||
221 | <div className={classes.label}> | ||
222 | {icon && <Icon path={icon} size={0.8} className={classes.icon} />} | ||
223 | {label} | ||
224 | </div> | ||
225 | </> | ||
226 | ); | ||
227 | |||
228 | const wrapperComponent = !href ? ( | ||
229 | <button | ||
230 | id={id} | ||
231 | type={type} | ||
232 | onClick={onClick} | ||
233 | className={classnames({ | ||
234 | [`${classes.button}`]: true, | ||
235 | [`${classes[buttonType as ButtonType]}`]: true, | ||
236 | [`${classes.disabled}`]: disabled, | ||
237 | [`${className}`]: className, | ||
238 | })} | ||
239 | disabled={disabled} | ||
240 | data-type="franz-button" | ||
241 | > | ||
242 | {content} | ||
243 | </button> | ||
244 | ) : ( | ||
245 | <a | ||
246 | href={href} | ||
247 | target={target} | ||
248 | onClick={onClick} | ||
249 | className={classnames({ | ||
250 | [`${classes.button}`]: true, | ||
251 | [`${classes[buttonType as ButtonType]}`]: true, | ||
252 | [`${className}`]: className, | ||
253 | })} | ||
254 | rel={target === '_blank' ? 'noopener' : ''} | ||
255 | data-type="franz-button" | ||
256 | > | ||
257 | {content} | ||
258 | </a> | ||
259 | ); | ||
260 | |||
261 | return wrapperComponent; | ||
262 | } | ||
263 | } | ||
264 | |||
265 | export const Button = injectStyle(styles)(withTheme(ButtonComponent)); | ||
diff --git a/src/components/ui/effects/Appear.js b/src/components/ui/effects/Appear.js deleted file mode 100644 index 183181f8f..000000000 --- a/src/components/ui/effects/Appear.js +++ /dev/null | |||
@@ -1,47 +0,0 @@ | |||
1 | import React, { Component } from 'react'; | ||
2 | import PropTypes from 'prop-types'; | ||
3 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; | ||
4 | |||
5 | export default class Appear extends Component { | ||
6 | static propTypes = { | ||
7 | // eslint-disable-next-line react/forbid-prop-types | ||
8 | children: PropTypes.any.isRequired, | ||
9 | transitionName: PropTypes.string, | ||
10 | className: PropTypes.string, | ||
11 | }; | ||
12 | |||
13 | static defaultProps = { | ||
14 | transitionName: 'fadeIn', | ||
15 | className: '', | ||
16 | }; | ||
17 | |||
18 | state = { | ||
19 | mounted: false, | ||
20 | }; | ||
21 | |||
22 | componentDidMount() { | ||
23 | this.setState({ mounted: true }); | ||
24 | } | ||
25 | |||
26 | render() { | ||
27 | const { children, transitionName, className } = this.props; | ||
28 | |||
29 | if (!this.state.mounted) { | ||
30 | return null; | ||
31 | } | ||
32 | |||
33 | return ( | ||
34 | <ReactCSSTransitionGroup | ||
35 | transitionName={transitionName} | ||
36 | transitionAppear | ||
37 | transitionLeave | ||
38 | transitionAppearTimeout={1500} | ||
39 | transitionEnterTimeout={1500} | ||
40 | transitionLeaveTimeout={1500} | ||
41 | className={className} | ||
42 | > | ||
43 | {children} | ||
44 | </ReactCSSTransitionGroup> | ||
45 | ); | ||
46 | } | ||
47 | } | ||
diff --git a/src/components/ui/effects/Appear.tsx b/src/components/ui/effects/Appear.tsx new file mode 100644 index 000000000..117c02f97 --- /dev/null +++ b/src/components/ui/effects/Appear.tsx | |||
@@ -0,0 +1,39 @@ | |||
1 | import { ReactChildren, useEffect, useState } from 'react'; | ||
2 | import ReactCSSTransitionGroup from 'react-addons-css-transition-group'; | ||
3 | |||
4 | type Props = { | ||
5 | children: ReactChildren; | ||
6 | transitionName: string; | ||
7 | className: string; | ||
8 | }; | ||
9 | const Appear = ({ | ||
10 | children, | ||
11 | transitionName = 'fadeIn', | ||
12 | className = '', | ||
13 | }: Props) => { | ||
14 | const [mounted, setMounted] = useState(false); | ||
15 | |||
16 | useEffect(() => { | ||
17 | setMounted(true); | ||
18 | }, []); | ||
19 | |||
20 | if (!mounted) { | ||
21 | return null; | ||
22 | } | ||
23 | |||
24 | return ( | ||
25 | <ReactCSSTransitionGroup | ||
26 | transitionName={transitionName} | ||
27 | transitionAppear | ||
28 | transitionLeave | ||
29 | transitionAppearTimeout={1500} | ||
30 | transitionEnterTimeout={1500} | ||
31 | transitionLeaveTimeout={1500} | ||
32 | className={className} | ||
33 | > | ||
34 | {children} | ||
35 | </ReactCSSTransitionGroup> | ||
36 | ); | ||
37 | }; | ||
38 | |||
39 | export default Appear; | ||
diff --git a/src/components/ui/error/index.tsx b/src/components/ui/error/index.tsx new file mode 100644 index 000000000..8439bfc8b --- /dev/null +++ b/src/components/ui/error/index.tsx | |||
@@ -0,0 +1,20 @@ | |||
1 | import { Classes } from 'jss'; | ||
2 | import { Component } from 'react'; | ||
3 | import injectSheet from 'react-jss'; | ||
4 | |||
5 | import styles from './styles'; | ||
6 | |||
7 | interface IProps { | ||
8 | classes: Classes; | ||
9 | message: string; | ||
10 | } | ||
11 | |||
12 | class ErrorComponent extends Component<IProps> { | ||
13 | render() { | ||
14 | const { classes, message } = this.props; | ||
15 | |||
16 | return <p className={classes.message}>{message}</p>; | ||
17 | } | ||
18 | } | ||
19 | |||
20 | export const Error = injectSheet(styles)(ErrorComponent); | ||
diff --git a/src/components/ui/error/styles.ts b/src/components/ui/error/styles.ts new file mode 100644 index 000000000..9da95705a --- /dev/null +++ b/src/components/ui/error/styles.ts | |||
@@ -0,0 +1,9 @@ | |||
1 | import { Theme } from '../../../themes'; | ||
2 | |||
3 | export default (theme: Theme) => ({ | ||
4 | message: { | ||
5 | color: theme.brandDanger, | ||
6 | margin: '5px 0 0', | ||
7 | fontSize: theme.uiFontSize, | ||
8 | }, | ||
9 | }); | ||
diff --git a/src/components/ui/headline/index.tsx b/src/components/ui/headline/index.tsx new file mode 100644 index 000000000..ea2949102 --- /dev/null +++ b/src/components/ui/headline/index.tsx | |||
@@ -0,0 +1,70 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, createElement, ReactNode } from 'react'; | ||
3 | import injectStyle from 'react-jss'; | ||
4 | |||
5 | import { Theme } from '../../../themes'; | ||
6 | import { IWithStyle, Omit } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | level?: number; | ||
10 | className?: string; | ||
11 | children: string | ReactNode; | ||
12 | id?: string; | ||
13 | } | ||
14 | |||
15 | const styles = (theme: Theme) => ({ | ||
16 | headline: { | ||
17 | fontWeight: 'lighter', | ||
18 | color: theme.colorText, | ||
19 | marginTop: 0, | ||
20 | marginBottom: 10, | ||
21 | textAlign: 'left', | ||
22 | }, | ||
23 | h1: { | ||
24 | fontSize: 30, | ||
25 | marginTop: 0, | ||
26 | }, | ||
27 | h2: { | ||
28 | fontSize: 20, | ||
29 | }, | ||
30 | h3: { | ||
31 | fontSize: 18, | ||
32 | }, | ||
33 | h4: { | ||
34 | fontSize: theme.uiFontSize, | ||
35 | }, | ||
36 | }); | ||
37 | |||
38 | class HeadlineComponent extends Component<IProps> { | ||
39 | render() { | ||
40 | const { classes, level, className, children, id } = this.props; | ||
41 | |||
42 | return createElement( | ||
43 | `h${level}`, | ||
44 | { | ||
45 | id, | ||
46 | className: classnames({ | ||
47 | [classes.headline]: true, | ||
48 | [classes[level ? `h${level}` : 'h1']]: true, | ||
49 | [`${className}`]: className, | ||
50 | }), | ||
51 | 'data-type': 'franz-headline', | ||
52 | }, | ||
53 | children, | ||
54 | ); | ||
55 | } | ||
56 | } | ||
57 | |||
58 | const Headline = injectStyle(styles)(HeadlineComponent); | ||
59 | |||
60 | const createH = (level: number) => (props: Omit<IProps, 'classes' | 'theme'>) => | ||
61 | ( | ||
62 | <Headline level={level} {...props}> | ||
63 | {props.children} | ||
64 | </Headline> | ||
65 | ); | ||
66 | |||
67 | export const H1 = createH(1); | ||
68 | export const H2 = createH(2); | ||
69 | export const H3 = createH(3); | ||
70 | export const H4 = createH(4); | ||
diff --git a/src/components/ui/icon/index.tsx b/src/components/ui/icon/index.tsx new file mode 100644 index 000000000..85bb61d13 --- /dev/null +++ b/src/components/ui/icon/index.tsx | |||
@@ -0,0 +1,46 @@ | |||
1 | import MdiIcon from '@mdi/react'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '../../../themes'; | ||
7 | import { IWithStyle } from '../typings/generic'; | ||
8 | |||
9 | interface IProps extends IWithStyle { | ||
10 | icon: string; | ||
11 | size?: number; | ||
12 | className?: string; | ||
13 | } | ||
14 | |||
15 | const styles = (theme: Theme) => ({ | ||
16 | icon: { | ||
17 | fill: theme.colorText, | ||
18 | }, | ||
19 | }); | ||
20 | |||
21 | class IconComponent extends Component<IProps> { | ||
22 | public static defaultProps = { | ||
23 | size: 1, | ||
24 | }; | ||
25 | |||
26 | render() { | ||
27 | const { classes, icon, size, className } = this.props; | ||
28 | |||
29 | if (!icon) { | ||
30 | console.warn('No Icon specified'); | ||
31 | } | ||
32 | |||
33 | return ( | ||
34 | <MdiIcon | ||
35 | path={icon} | ||
36 | size={size} | ||
37 | className={classnames({ | ||
38 | [classes.icon]: true, | ||
39 | [`${className}`]: className, | ||
40 | })} | ||
41 | /> | ||
42 | ); | ||
43 | } | ||
44 | } | ||
45 | |||
46 | export const Icon = injectStyle(styles)(IconComponent); | ||
diff --git a/src/components/ui/infobox/index.tsx b/src/components/ui/infobox/index.tsx new file mode 100644 index 000000000..87940c4d4 --- /dev/null +++ b/src/components/ui/infobox/index.tsx | |||
@@ -0,0 +1,205 @@ | |||
1 | import { mdiClose } from '@mdi/js'; | ||
2 | import classnames from 'classnames'; | ||
3 | import { Component, ReactNode } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '../../../themes'; | ||
7 | import { Icon } from '../icon'; | ||
8 | import { IWithStyle } from '../typings/generic'; | ||
9 | |||
10 | interface IProps extends IWithStyle { | ||
11 | icon?: string; | ||
12 | type?: string; | ||
13 | dismissable?: boolean; | ||
14 | onDismiss?: () => void; | ||
15 | onUnmount?: () => void; | ||
16 | ctaOnClick?: () => void; | ||
17 | ctaLabel?: string; | ||
18 | ctaLoading?: boolean; | ||
19 | children: ReactNode; | ||
20 | className: string; | ||
21 | } | ||
22 | |||
23 | interface IState { | ||
24 | isDismissing: boolean; | ||
25 | dismissed: boolean; | ||
26 | } | ||
27 | |||
28 | const buttonStyles = (theme: Theme) => { | ||
29 | const styles = {}; | ||
30 | Object.keys(theme.styleTypes).map(style => { | ||
31 | Object.assign(styles, { | ||
32 | [style]: { | ||
33 | background: theme.styleTypes[style].accent, | ||
34 | color: theme.styleTypes[style].contrast, | ||
35 | border: theme.styleTypes[style].border, | ||
36 | |||
37 | '& svg': { | ||
38 | fill: theme.styleTypes[style].contrast, | ||
39 | }, | ||
40 | }, | ||
41 | }); | ||
42 | }); | ||
43 | |||
44 | return styles; | ||
45 | }; | ||
46 | |||
47 | const infoBoxTransition: string = 'none'; | ||
48 | const ctaTransition: string = 'none'; | ||
49 | |||
50 | // TODO: Not sure why, but this location alone, the `dinwo` is not defined - and it throws an error thus aborting the startup sequence of ferdi | ||
51 | // if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
52 | // infoBoxTransition = 'all 0.5s'; | ||
53 | // ctaTransition = 'opacity 0.3s'; | ||
54 | // } | ||
55 | |||
56 | const styles = (theme: Theme) => ({ | ||
57 | wrapper: { | ||
58 | position: 'relative', | ||
59 | overflow: 'hidden', | ||
60 | height: 'auto', | ||
61 | marginBottom: 30, | ||
62 | }, | ||
63 | infobox: { | ||
64 | alignItems: 'center', | ||
65 | borderRadius: theme.borderRadiusSmall, | ||
66 | display: 'flex', | ||
67 | height: 'auto', | ||
68 | padding: '15px 20px', | ||
69 | top: 0, | ||
70 | transition: infoBoxTransition, | ||
71 | opacity: 1, | ||
72 | }, | ||
73 | dismissing: { | ||
74 | // position: 'absolute', | ||
75 | marginTop: -100, | ||
76 | opacity: 0, | ||
77 | }, | ||
78 | content: { | ||
79 | flex: 1, | ||
80 | }, | ||
81 | icon: { | ||
82 | marginRight: 10, | ||
83 | }, | ||
84 | close: { | ||
85 | color: (props: IProps) => | ||
86 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
87 | marginRight: -5, | ||
88 | border: 0, | ||
89 | background: 'none', | ||
90 | }, | ||
91 | cta: { | ||
92 | borderColor: (props: IProps) => | ||
93 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
94 | borderRadius: theme.borderRadiusSmall, | ||
95 | borderStyle: 'solid', | ||
96 | borderWidth: 1, | ||
97 | background: 'none', | ||
98 | color: (props: IProps) => | ||
99 | theme.styleTypes[props.type ? props.type : 'primary'].contrast, | ||
100 | marginLeft: 15, | ||
101 | padding: [4, 10], | ||
102 | fontSize: theme.uiFontSize, | ||
103 | transition: ctaTransition, | ||
104 | |||
105 | '&:hover': { | ||
106 | opacity: 0.6, | ||
107 | }, | ||
108 | }, | ||
109 | ...buttonStyles(theme), | ||
110 | }); | ||
111 | |||
112 | class InfoboxComponent extends Component<IProps, IState> { | ||
113 | public static defaultProps = { | ||
114 | type: 'primary', | ||
115 | dismissable: false, | ||
116 | ctaOnClick: () => {}, | ||
117 | onDismiss: () => {}, | ||
118 | ctaLabel: '', | ||
119 | ctaLoading: false, | ||
120 | }; | ||
121 | |||
122 | state = { | ||
123 | isDismissing: false, | ||
124 | dismissed: false, | ||
125 | }; | ||
126 | |||
127 | dismiss() { | ||
128 | const { onDismiss } = this.props; | ||
129 | |||
130 | this.setState({ | ||
131 | isDismissing: true, | ||
132 | }); | ||
133 | |||
134 | if (onDismiss) { | ||
135 | onDismiss(); | ||
136 | } | ||
137 | |||
138 | setTimeout(() => { | ||
139 | this.setState({ | ||
140 | dismissed: true, | ||
141 | }); | ||
142 | }, 3000); | ||
143 | } | ||
144 | |||
145 | componentWillUnmount(): void { | ||
146 | const { onUnmount } = this.props; | ||
147 | if (onUnmount) onUnmount(); | ||
148 | } | ||
149 | |||
150 | render() { | ||
151 | const { | ||
152 | classes, | ||
153 | children, | ||
154 | icon, | ||
155 | type, | ||
156 | ctaLabel, | ||
157 | ctaOnClick, | ||
158 | dismissable, | ||
159 | className, | ||
160 | } = this.props; | ||
161 | |||
162 | const { isDismissing, dismissed } = this.state; | ||
163 | |||
164 | if (dismissed) { | ||
165 | return null; | ||
166 | } | ||
167 | |||
168 | return ( | ||
169 | <div | ||
170 | className={classnames({ | ||
171 | [classes.wrapper]: true, | ||
172 | [`${className}`]: className, | ||
173 | })} | ||
174 | > | ||
175 | <div | ||
176 | className={classnames({ | ||
177 | [classes.infobox]: true, | ||
178 | [classes[`${type}`]]: type, | ||
179 | [classes.dismissing]: isDismissing, | ||
180 | })} | ||
181 | data-type="franz-infobox" | ||
182 | > | ||
183 | {icon && <Icon icon={icon} className={classes.icon} />} | ||
184 | <div className={classes.content}>{children}</div> | ||
185 | {ctaLabel && ( | ||
186 | <button className={classes.cta} onClick={ctaOnClick} type="button"> | ||
187 | {ctaLabel} | ||
188 | </button> | ||
189 | )} | ||
190 | {dismissable && ( | ||
191 | <button | ||
192 | type="button" | ||
193 | onClick={this.dismiss.bind(this)} | ||
194 | className={classes.close} | ||
195 | > | ||
196 | <Icon icon={mdiClose} /> | ||
197 | </button> | ||
198 | )} | ||
199 | </div> | ||
200 | </div> | ||
201 | ); | ||
202 | } | ||
203 | } | ||
204 | |||
205 | export const Infobox = injectStyle(styles)(InfoboxComponent); | ||
diff --git a/src/components/ui/input/index.tsx b/src/components/ui/input/index.tsx new file mode 100644 index 000000000..0b16fe688 --- /dev/null +++ b/src/components/ui/input/index.tsx | |||
@@ -0,0 +1,208 @@ | |||
1 | import { mdiEye, mdiEyeOff } from '@mdi/js'; | ||
2 | import Icon from '@mdi/react'; | ||
3 | import classnames from 'classnames'; | ||
4 | import { Component, createRef, InputHTMLAttributes } from 'react'; | ||
5 | import injectSheet from 'react-jss'; | ||
6 | |||
7 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
8 | |||
9 | import { Error } from '../error'; | ||
10 | import { Label } from '../label'; | ||
11 | import { Wrapper } from '../wrapper'; | ||
12 | import { scorePasswordFunc } from './scorePassword'; | ||
13 | |||
14 | import styles from './styles'; | ||
15 | |||
16 | interface IData { | ||
17 | [index: string]: string; | ||
18 | } | ||
19 | |||
20 | interface IProps | ||
21 | extends InputHTMLAttributes<HTMLInputElement>, | ||
22 | IFormField, | ||
23 | IWithStyle { | ||
24 | focus?: boolean; | ||
25 | prefix?: string; | ||
26 | suffix?: string; | ||
27 | scorePassword?: boolean; | ||
28 | showPasswordToggle?: boolean; | ||
29 | data: IData; | ||
30 | inputClassName?: string; | ||
31 | onEnterKey?: Function; | ||
32 | } | ||
33 | |||
34 | interface IState { | ||
35 | showPassword: boolean; | ||
36 | passwordScore: number; | ||
37 | } | ||
38 | |||
39 | class InputComponent extends Component<IProps, IState> { | ||
40 | static defaultProps = { | ||
41 | focus: false, | ||
42 | onChange: () => {}, | ||
43 | onBlur: () => {}, | ||
44 | onFocus: () => {}, | ||
45 | scorePassword: false, | ||
46 | showLabel: true, | ||
47 | showPasswordToggle: false, | ||
48 | type: 'text', | ||
49 | disabled: false, | ||
50 | }; | ||
51 | |||
52 | state = { | ||
53 | passwordScore: 0, | ||
54 | showPassword: false, | ||
55 | }; | ||
56 | |||
57 | private inputRef = createRef<HTMLInputElement>(); | ||
58 | |||
59 | componentDidMount() { | ||
60 | const { focus, data } = this.props; | ||
61 | |||
62 | if (this.inputRef && this.inputRef.current) { | ||
63 | if (focus) { | ||
64 | this.inputRef.current.focus(); | ||
65 | } | ||
66 | |||
67 | if (data) { | ||
68 | Object.keys(data).map( | ||
69 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
70 | ); | ||
71 | } | ||
72 | } | ||
73 | } | ||
74 | |||
75 | onChange(e: React.ChangeEvent<HTMLInputElement>) { | ||
76 | const { scorePassword, onChange } = this.props; | ||
77 | |||
78 | if (onChange) { | ||
79 | onChange(e); | ||
80 | } | ||
81 | |||
82 | if (this.inputRef && this.inputRef.current && scorePassword) { | ||
83 | this.setState({ | ||
84 | passwordScore: scorePasswordFunc(this.inputRef.current.value), | ||
85 | }); | ||
86 | } | ||
87 | } | ||
88 | |||
89 | onInputKeyPress(e: React.KeyboardEvent) { | ||
90 | if (e.key === 'Enter') { | ||
91 | const { onEnterKey } = this.props; | ||
92 | onEnterKey && onEnterKey(); | ||
93 | } | ||
94 | } | ||
95 | |||
96 | render() { | ||
97 | const { | ||
98 | classes, | ||
99 | className, | ||
100 | disabled, | ||
101 | error, | ||
102 | id, | ||
103 | inputClassName, | ||
104 | label, | ||
105 | prefix, | ||
106 | scorePassword, | ||
107 | suffix, | ||
108 | showLabel, | ||
109 | showPasswordToggle, | ||
110 | type, | ||
111 | value, | ||
112 | name, | ||
113 | placeholder, | ||
114 | spellCheck, | ||
115 | onBlur, | ||
116 | onFocus, | ||
117 | min, | ||
118 | max, | ||
119 | step, | ||
120 | required, | ||
121 | noMargin, | ||
122 | } = this.props; | ||
123 | |||
124 | const { showPassword, passwordScore } = this.state; | ||
125 | |||
126 | const inputType = type === 'password' && showPassword ? 'text' : type; | ||
127 | |||
128 | return ( | ||
129 | <Wrapper | ||
130 | className={className} | ||
131 | identifier="franz-input" | ||
132 | noMargin={noMargin} | ||
133 | > | ||
134 | <Label | ||
135 | title={label} | ||
136 | showLabel={showLabel} | ||
137 | htmlFor={id} | ||
138 | className={classes.label} | ||
139 | isRequired={required} | ||
140 | > | ||
141 | <div | ||
142 | className={classnames({ | ||
143 | [`${inputClassName}`]: inputClassName, | ||
144 | [`${classes.hasPasswordScore}`]: scorePassword, | ||
145 | [`${classes.wrapper}`]: true, | ||
146 | [`${classes.disabled}`]: disabled, | ||
147 | [`${classes.hasError}`]: error, | ||
148 | })} | ||
149 | > | ||
150 | {prefix && <span className={classes.prefix}>{prefix}</span>} | ||
151 | <input | ||
152 | id={id} | ||
153 | type={inputType} | ||
154 | name={name} | ||
155 | value={value as string} | ||
156 | placeholder={placeholder} | ||
157 | spellCheck={spellCheck} | ||
158 | className={classes.input} | ||
159 | ref={this.inputRef} | ||
160 | onChange={this.onChange.bind(this)} | ||
161 | onFocus={onFocus} | ||
162 | onBlur={onBlur} | ||
163 | disabled={disabled} | ||
164 | onKeyPress={this.onInputKeyPress.bind(this)} | ||
165 | min={min} | ||
166 | max={max} | ||
167 | step={step} | ||
168 | /> | ||
169 | {suffix && <span className={classes.suffix}>{suffix}</span>} | ||
170 | {showPasswordToggle && ( | ||
171 | <button | ||
172 | type="button" | ||
173 | className={classes.formModifier} | ||
174 | onClick={() => | ||
175 | this.setState(prevState => ({ | ||
176 | showPassword: !prevState.showPassword, | ||
177 | })) | ||
178 | } | ||
179 | tabIndex={-1} | ||
180 | > | ||
181 | <Icon path={!showPassword ? mdiEye : mdiEyeOff} size={1} /> | ||
182 | </button> | ||
183 | )} | ||
184 | </div> | ||
185 | {scorePassword && ( | ||
186 | <div | ||
187 | className={classnames({ | ||
188 | [`${classes.passwordScore}`]: true, | ||
189 | [`${classes.hasError}`]: error, | ||
190 | })} | ||
191 | > | ||
192 | <meter | ||
193 | value={passwordScore < 5 ? 5 : passwordScore} | ||
194 | low={30} | ||
195 | high={75} | ||
196 | optimum={100} | ||
197 | max={100} | ||
198 | /> | ||
199 | </div> | ||
200 | )} | ||
201 | </Label> | ||
202 | {error && <Error message={error} />} | ||
203 | </Wrapper> | ||
204 | ); | ||
205 | } | ||
206 | } | ||
207 | |||
208 | export const Input = injectSheet(styles)(InputComponent); | ||
diff --git a/src/components/ui/input/scorePassword.ts b/src/components/ui/input/scorePassword.ts new file mode 100644 index 000000000..59502e2b0 --- /dev/null +++ b/src/components/ui/input/scorePassword.ts | |||
@@ -0,0 +1,42 @@ | |||
1 | interface ILetters { | ||
2 | [key: string]: number; | ||
3 | } | ||
4 | |||
5 | interface IVariations { | ||
6 | [index: string]: boolean; | ||
7 | digits: boolean; | ||
8 | lower: boolean; | ||
9 | nonWords: boolean; | ||
10 | upper: boolean; | ||
11 | } | ||
12 | |||
13 | export function scorePasswordFunc(password: string): number { | ||
14 | let score = 0; | ||
15 | if (!password) { | ||
16 | return score; | ||
17 | } | ||
18 | |||
19 | // award every unique letter until 5 repetitions | ||
20 | const letters: ILetters = {}; | ||
21 | for (const element of password) { | ||
22 | letters[element] = (letters[element] || 0) + 1; | ||
23 | score += 5 / letters[element]; | ||
24 | } | ||
25 | |||
26 | // bonus points for mixing it up | ||
27 | const variations: IVariations = { | ||
28 | digits: /\d/.test(password), | ||
29 | lower: /[a-z]/.test(password), | ||
30 | nonWords: /\W/.test(password), | ||
31 | upper: /[A-Z]/.test(password), | ||
32 | }; | ||
33 | |||
34 | let variationCount = 0; | ||
35 | for (const key of Object.keys(variations)) { | ||
36 | variationCount += variations[key] === true ? 1 : 0; | ||
37 | } | ||
38 | |||
39 | score += (variationCount - 1) * 10; | ||
40 | |||
41 | return Math.round(score); | ||
42 | } | ||
diff --git a/src/components/ui/input/styles.ts b/src/components/ui/input/styles.ts new file mode 100644 index 000000000..04c1b3991 --- /dev/null +++ b/src/components/ui/input/styles.ts | |||
@@ -0,0 +1,102 @@ | |||
1 | import { Property } from 'csstype'; | ||
2 | |||
3 | import { Theme } from '../../../themes'; | ||
4 | |||
5 | const prefixStyles = (theme: Theme) => ({ | ||
6 | background: theme.inputPrefixBackground, | ||
7 | color: theme.inputPrefixColor, | ||
8 | lineHeight: `${theme.inputHeight}px`, | ||
9 | padding: '0 10px', | ||
10 | fontSize: theme.uiFontSize, | ||
11 | }); | ||
12 | |||
13 | export default (theme: Theme) => ({ | ||
14 | label: { | ||
15 | '& > div': { | ||
16 | marginTop: 5, | ||
17 | }, | ||
18 | }, | ||
19 | disabled: { | ||
20 | opacity: theme.inputDisabledOpacity, | ||
21 | }, | ||
22 | formModifier: { | ||
23 | background: 'none', | ||
24 | border: 0, | ||
25 | borderLeft: theme.inputBorder, | ||
26 | padding: '4px 20px 0', | ||
27 | outline: 'none', | ||
28 | |||
29 | '&:active': { | ||
30 | opacity: 0.5, | ||
31 | }, | ||
32 | |||
33 | '& svg': { | ||
34 | fill: theme.inputModifierColor, | ||
35 | }, | ||
36 | }, | ||
37 | input: { | ||
38 | background: 'none', | ||
39 | border: 0, | ||
40 | fontSize: theme.uiFontSize, | ||
41 | outline: 'none', | ||
42 | padding: 8, | ||
43 | width: '100%', | ||
44 | color: theme.inputColor, | ||
45 | |||
46 | '&::placeholder': { | ||
47 | color: theme.inputPlaceholderColor, | ||
48 | }, | ||
49 | }, | ||
50 | passwordScore: { | ||
51 | background: theme.inputScorePasswordBackground, | ||
52 | border: theme.inputBorder, | ||
53 | borderTopWidth: 0, | ||
54 | borderBottomLeftRadius: theme.borderRadiusSmall, | ||
55 | borderBottomRightRadius: theme.borderRadiusSmall, | ||
56 | display: 'block', | ||
57 | flexBasis: '100%', | ||
58 | height: 5, | ||
59 | overflow: 'hidden', | ||
60 | |||
61 | '& meter': { | ||
62 | display: 'block', | ||
63 | height: '100%', | ||
64 | width: '100%', | ||
65 | |||
66 | '&::-webkit-meter-bar': { | ||
67 | background: 'none', | ||
68 | }, | ||
69 | |||
70 | '&::-webkit-meter-even-less-good-value': { | ||
71 | background: theme.brandDanger, | ||
72 | }, | ||
73 | |||
74 | '&::-webkit-meter-suboptimum-value': { | ||
75 | background: theme.brandWarning, | ||
76 | }, | ||
77 | |||
78 | '&::-webkit-meter-optimum-value': { | ||
79 | background: theme.brandSuccess, | ||
80 | }, | ||
81 | }, | ||
82 | }, | ||
83 | prefix: prefixStyles(theme), | ||
84 | suffix: prefixStyles(theme), | ||
85 | wrapper: { | ||
86 | background: theme.inputBackground, | ||
87 | border: theme.inputBorder, | ||
88 | borderRadius: theme.borderRadiusSmall, | ||
89 | boxSizing: 'border-box' as Property.BoxSizing, | ||
90 | display: 'flex', | ||
91 | height: theme.inputHeight, | ||
92 | order: 1, | ||
93 | width: '100%', | ||
94 | }, | ||
95 | hasPasswordScore: { | ||
96 | borderBottomLeftRadius: 0, | ||
97 | borderBottomRightRadius: 0, | ||
98 | }, | ||
99 | hasError: { | ||
100 | borderColor: theme.brandDanger, | ||
101 | }, | ||
102 | }); | ||
diff --git a/src/components/ui/label/index.tsx b/src/components/ui/label/index.tsx new file mode 100644 index 000000000..4d86f23f7 --- /dev/null +++ b/src/components/ui/label/index.tsx | |||
@@ -0,0 +1,52 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Classes } from 'jss'; | ||
3 | import { Component, LabelHTMLAttributes } from 'react'; | ||
4 | import injectSheet from 'react-jss'; | ||
5 | |||
6 | import { IFormField } from '../typings/generic'; | ||
7 | |||
8 | import styles from './styles'; | ||
9 | |||
10 | interface ILabel extends IFormField, LabelHTMLAttributes<HTMLLabelElement> { | ||
11 | classes: Classes; | ||
12 | isRequired: boolean; | ||
13 | } | ||
14 | |||
15 | class LabelComponent extends Component<ILabel> { | ||
16 | static defaultProps = { | ||
17 | showLabel: true, | ||
18 | }; | ||
19 | |||
20 | render() { | ||
21 | const { | ||
22 | title, | ||
23 | showLabel, | ||
24 | classes, | ||
25 | className, | ||
26 | children, | ||
27 | htmlFor, | ||
28 | isRequired, | ||
29 | } = this.props; | ||
30 | |||
31 | if (!showLabel) return children; | ||
32 | |||
33 | return ( | ||
34 | <label | ||
35 | className={classnames({ | ||
36 | [`${className}`]: className, | ||
37 | })} | ||
38 | htmlFor={htmlFor} | ||
39 | > | ||
40 | {showLabel && ( | ||
41 | <span className={classes.label}> | ||
42 | {title} | ||
43 | {isRequired && ' *'} | ||
44 | </span> | ||
45 | )} | ||
46 | <div className={classes.content}>{children}</div> | ||
47 | </label> | ||
48 | ); | ||
49 | } | ||
50 | } | ||
51 | |||
52 | export const Label = injectSheet(styles)(LabelComponent); | ||
diff --git a/src/components/ui/label/styles.ts b/src/components/ui/label/styles.ts new file mode 100644 index 000000000..faa44ae5b --- /dev/null +++ b/src/components/ui/label/styles.ts | |||
@@ -0,0 +1,12 @@ | |||
1 | import { Theme } from '../../../themes'; | ||
2 | |||
3 | export default (theme: Theme) => ({ | ||
4 | content: {}, | ||
5 | label: { | ||
6 | color: theme.labelColor, | ||
7 | fontSize: theme.uiFontSize, | ||
8 | }, | ||
9 | hasError: { | ||
10 | color: theme.brandDanger, | ||
11 | }, | ||
12 | }); | ||
diff --git a/src/components/ui/loader/index.tsx b/src/components/ui/loader/index.tsx new file mode 100644 index 000000000..0607bd48b --- /dev/null +++ b/src/components/ui/loader/index.tsx | |||
@@ -0,0 +1,44 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component } from 'react'; | ||
3 | import injectStyle, { withTheme } from 'react-jss'; | ||
4 | import ReactLoader from 'react-loader'; | ||
5 | |||
6 | import { IWithStyle } from '../typings/generic'; | ||
7 | |||
8 | interface IProps extends IWithStyle { | ||
9 | className?: string; | ||
10 | color?: string; | ||
11 | } | ||
12 | |||
13 | const styles = () => ({ | ||
14 | container: { | ||
15 | position: 'relative', | ||
16 | height: 60, | ||
17 | }, | ||
18 | }); | ||
19 | |||
20 | class LoaderComponent extends Component<IProps> { | ||
21 | render() { | ||
22 | const { classes, className, color, theme } = this.props; | ||
23 | |||
24 | return ( | ||
25 | <div | ||
26 | className={classnames({ | ||
27 | [classes.container]: true, | ||
28 | [`${className}`]: className, | ||
29 | })} | ||
30 | data-type="franz-loader" | ||
31 | > | ||
32 | <ReactLoader | ||
33 | loaded={false} | ||
34 | width={4} | ||
35 | scale={0.75} | ||
36 | color={color || theme.colorText} | ||
37 | parentClassName={classes.loader} | ||
38 | /> | ||
39 | </div> | ||
40 | ); | ||
41 | } | ||
42 | } | ||
43 | |||
44 | export const Loader = injectStyle(styles)(withTheme(LoaderComponent)); | ||
diff --git a/src/components/ui/select/index.tsx b/src/components/ui/select/index.tsx new file mode 100644 index 000000000..2605503a3 --- /dev/null +++ b/src/components/ui/select/index.tsx | |||
@@ -0,0 +1,460 @@ | |||
1 | import { | ||
2 | mdiArrowRightDropCircleOutline, | ||
3 | mdiCloseCircle, | ||
4 | mdiMagnify, | ||
5 | } from '@mdi/js'; | ||
6 | import Icon from '@mdi/react'; | ||
7 | import classnames from 'classnames'; | ||
8 | import { ChangeEvent, Component, createRef } from 'react'; | ||
9 | import injectStyle from 'react-jss'; | ||
10 | |||
11 | import { Theme } from '../../../themes'; | ||
12 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
13 | |||
14 | import { Error } from '../error'; | ||
15 | import { Label } from '../label'; | ||
16 | import { Wrapper } from '../wrapper'; | ||
17 | |||
18 | interface IOptions { | ||
19 | [index: string]: string; | ||
20 | } | ||
21 | |||
22 | interface IData { | ||
23 | [index: string]: string; | ||
24 | } | ||
25 | |||
26 | interface IProps extends IFormField, IWithStyle { | ||
27 | actionText: string; | ||
28 | className?: string; | ||
29 | inputClassName?: string; | ||
30 | defaultValue?: string; | ||
31 | disabled?: boolean; | ||
32 | id?: string; | ||
33 | name: string; | ||
34 | options: IOptions; | ||
35 | value: string; | ||
36 | onChange: (event: ChangeEvent<HTMLInputElement>) => void; | ||
37 | showSearch: boolean; | ||
38 | data: IData; | ||
39 | } | ||
40 | |||
41 | interface IState { | ||
42 | open: boolean; | ||
43 | value: string; | ||
44 | needle: string; | ||
45 | selected: number; | ||
46 | options: IOptions; | ||
47 | } | ||
48 | |||
49 | let popupTransition: string = 'none'; | ||
50 | let toggleTransition: string = 'none'; | ||
51 | |||
52 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
53 | popupTransition = 'all 0.3s'; | ||
54 | toggleTransition = 'transform 0.3s'; | ||
55 | } | ||
56 | |||
57 | const styles = (theme: Theme) => ({ | ||
58 | select: { | ||
59 | background: theme.selectBackground, | ||
60 | border: theme.selectBorder, | ||
61 | borderRadius: theme.borderRadiusSmall, | ||
62 | height: theme.selectHeight, | ||
63 | fontSize: theme.uiFontSize, | ||
64 | width: '100%', | ||
65 | display: 'flex', | ||
66 | alignItems: 'center', | ||
67 | textAlign: 'left', | ||
68 | color: theme.selectColor, | ||
69 | }, | ||
70 | label: { | ||
71 | '& > div': { | ||
72 | marginTop: 5, | ||
73 | }, | ||
74 | }, | ||
75 | popup: { | ||
76 | opacity: 0, | ||
77 | height: 0, | ||
78 | overflowX: 'scroll', | ||
79 | border: theme.selectBorder, | ||
80 | borderTop: 0, | ||
81 | transition: popupTransition, | ||
82 | }, | ||
83 | open: { | ||
84 | opacity: 1, | ||
85 | height: 350, | ||
86 | background: theme.selectPopupBackground, | ||
87 | }, | ||
88 | option: { | ||
89 | padding: 10, | ||
90 | borderBottom: theme.selectOptionBorder, | ||
91 | color: theme.selectOptionColor, | ||
92 | |||
93 | '&:hover': { | ||
94 | background: theme.selectOptionItemHover, | ||
95 | color: theme.selectOptionItemHoverColor, | ||
96 | }, | ||
97 | '&:active': { | ||
98 | background: theme.selectOptionItemActive, | ||
99 | color: theme.selectOptionItemActiveColor, | ||
100 | }, | ||
101 | }, | ||
102 | selected: { | ||
103 | background: theme.selectOptionItemActive, | ||
104 | color: theme.selectOptionItemActiveColor, | ||
105 | }, | ||
106 | toggle: { | ||
107 | marginLeft: 'auto', | ||
108 | fill: theme.selectToggleColor, | ||
109 | transition: toggleTransition, | ||
110 | }, | ||
111 | toggleOpened: { | ||
112 | transform: 'rotateZ(90deg)', | ||
113 | }, | ||
114 | searchContainer: { | ||
115 | display: 'flex', | ||
116 | background: theme.selectSearchBackground, | ||
117 | alignItems: 'center', | ||
118 | paddingLeft: 10, | ||
119 | color: theme.selectColor, | ||
120 | |||
121 | '& svg': { | ||
122 | fill: theme.selectSearchColor, | ||
123 | }, | ||
124 | }, | ||
125 | search: { | ||
126 | border: 0, | ||
127 | width: '100%', | ||
128 | fontSize: theme.uiFontSize, | ||
129 | background: 'none', | ||
130 | marginLeft: 10, | ||
131 | padding: [10, 0], | ||
132 | color: theme.selectSearchColor, | ||
133 | }, | ||
134 | clearNeedle: { | ||
135 | background: 'none', | ||
136 | border: 0, | ||
137 | }, | ||
138 | focused: { | ||
139 | fontWeight: 'bold', | ||
140 | background: theme.selectOptionItemHover, | ||
141 | color: theme.selectOptionItemHoverColor, | ||
142 | }, | ||
143 | hasError: { | ||
144 | borderColor: theme.brandDanger, | ||
145 | }, | ||
146 | disabled: { | ||
147 | opacity: theme.selectDisabledOpacity, | ||
148 | }, | ||
149 | }); | ||
150 | |||
151 | class SelectComponent extends Component<IProps> { | ||
152 | public static defaultProps = { | ||
153 | onChange: () => {}, | ||
154 | showLabel: true, | ||
155 | disabled: false, | ||
156 | error: '', | ||
157 | }; | ||
158 | |||
159 | state = { | ||
160 | open: false, | ||
161 | value: '', | ||
162 | needle: '', | ||
163 | selected: 0, | ||
164 | options: null, | ||
165 | }; | ||
166 | |||
167 | private componentRef = createRef<HTMLDivElement>(); | ||
168 | |||
169 | private inputRef = createRef<HTMLInputElement>(); | ||
170 | |||
171 | private searchInputRef = createRef<HTMLInputElement>(); | ||
172 | |||
173 | private scrollContainerRef = createRef<HTMLDivElement>(); | ||
174 | |||
175 | private activeOptionRef = createRef<HTMLDivElement>(); | ||
176 | |||
177 | private keyListener: any; | ||
178 | |||
179 | componentWillReceiveProps(nextProps: IProps) { | ||
180 | if (nextProps.value && nextProps.value !== this.props.value) { | ||
181 | this.setState({ | ||
182 | value: nextProps.value, | ||
183 | }); | ||
184 | } | ||
185 | } | ||
186 | |||
187 | componentDidUpdate() { | ||
188 | const { open } = this.state; | ||
189 | |||
190 | if (this.searchInputRef && this.searchInputRef.current && open) { | ||
191 | this.searchInputRef.current.focus(); | ||
192 | } | ||
193 | } | ||
194 | |||
195 | componentDidMount() { | ||
196 | if (this.inputRef && this.inputRef.current) { | ||
197 | const { data } = this.props; | ||
198 | |||
199 | if (data) { | ||
200 | Object.keys(data).map( | ||
201 | key => (this.inputRef.current!.dataset[key] = data[key]), | ||
202 | ); | ||
203 | } | ||
204 | } | ||
205 | |||
206 | window.addEventListener('keydown', this.arrowKeysHandler.bind(this), false); | ||
207 | } | ||
208 | |||
209 | componentWillMount() { | ||
210 | const { value } = this.props; | ||
211 | |||
212 | if (this.componentRef && this.componentRef.current) { | ||
213 | this.componentRef.current.removeEventListener( | ||
214 | 'keydown', | ||
215 | this.keyListener, | ||
216 | ); | ||
217 | } | ||
218 | |||
219 | if (value) { | ||
220 | this.setState({ | ||
221 | value, | ||
222 | }); | ||
223 | } | ||
224 | |||
225 | this.setFilter(); | ||
226 | } | ||
227 | |||
228 | componentWillUnmount() { | ||
229 | // eslint-disable-next-line unicorn/no-invalid-remove-event-listener | ||
230 | window.removeEventListener('keydown', this.arrowKeysHandler.bind(this)); | ||
231 | } | ||
232 | |||
233 | setFilter(needle = '') { | ||
234 | const { options } = this.props; | ||
235 | |||
236 | let filteredOptions = {}; | ||
237 | if (needle) { | ||
238 | Object.keys(options).map(key => { | ||
239 | if ( | ||
240 | key.toLocaleLowerCase().startsWith(needle.toLocaleLowerCase()) || | ||
241 | options[key] | ||
242 | .toLocaleLowerCase() | ||
243 | .startsWith(needle.toLocaleLowerCase()) | ||
244 | ) { | ||
245 | Object.assign(filteredOptions, { | ||
246 | [`${key}`]: options[key], | ||
247 | }); | ||
248 | } | ||
249 | }); | ||
250 | } else { | ||
251 | filteredOptions = options; | ||
252 | } | ||
253 | |||
254 | this.setState({ | ||
255 | needle, | ||
256 | options: filteredOptions, | ||
257 | selected: 0, | ||
258 | }); | ||
259 | } | ||
260 | |||
261 | select(key: string) { | ||
262 | this.setState(() => ({ | ||
263 | value: key, | ||
264 | open: false, | ||
265 | })); | ||
266 | |||
267 | this.setFilter(); | ||
268 | |||
269 | if (this.props.onChange) { | ||
270 | this.props.onChange(key as any); | ||
271 | } | ||
272 | } | ||
273 | |||
274 | arrowKeysHandler(e: KeyboardEvent) { | ||
275 | const { selected, open, options } = this.state; | ||
276 | |||
277 | if (!open) return; | ||
278 | |||
279 | if (e.keyCode === 38 || e.keyCode === 40) { | ||
280 | e.preventDefault(); | ||
281 | } | ||
282 | |||
283 | if (this.componentRef && this.componentRef.current) { | ||
284 | if (e.keyCode === 38 && selected > 0) { | ||
285 | this.setState((state: IState) => ({ | ||
286 | selected: state.selected - 1, | ||
287 | })); | ||
288 | } else if ( | ||
289 | e.keyCode === 40 && | ||
290 | selected < Object.keys(options!).length - 1 | ||
291 | ) { | ||
292 | this.setState((state: IState) => ({ | ||
293 | selected: state.selected + 1, | ||
294 | })); | ||
295 | } else if (e.keyCode === 13) { | ||
296 | this.select(Object.keys(options!)[selected]); | ||
297 | } | ||
298 | |||
299 | if ( | ||
300 | this.activeOptionRef && | ||
301 | this.activeOptionRef.current && | ||
302 | this.scrollContainerRef && | ||
303 | this.scrollContainerRef.current | ||
304 | ) { | ||
305 | const containerTopOffset = this.scrollContainerRef.current.offsetTop; | ||
306 | const optionTopOffset = this.activeOptionRef.current.offsetTop; | ||
307 | |||
308 | const topOffset = optionTopOffset - containerTopOffset; | ||
309 | |||
310 | this.scrollContainerRef.current.scrollTop = topOffset - 35; | ||
311 | } | ||
312 | } | ||
313 | |||
314 | switch (e.keyCode) { | ||
315 | case 37: | ||
316 | case 39: | ||
317 | case 38: | ||
318 | case 40: // Arrow keys | ||
319 | case 32: | ||
320 | break; // Space | ||
321 | default: | ||
322 | break; // do not block other keys | ||
323 | } | ||
324 | } | ||
325 | |||
326 | render() { | ||
327 | const { | ||
328 | actionText, | ||
329 | classes, | ||
330 | className, | ||
331 | defaultValue, | ||
332 | disabled, | ||
333 | error, | ||
334 | id, | ||
335 | inputClassName, | ||
336 | name, | ||
337 | label, | ||
338 | showLabel, | ||
339 | showSearch, | ||
340 | onChange, | ||
341 | required, | ||
342 | } = this.props; | ||
343 | |||
344 | const { open, needle, value, selected, options } = this.state; | ||
345 | |||
346 | let selection = ''; | ||
347 | if (!value && defaultValue && options![defaultValue]) { | ||
348 | selection = options![defaultValue]; | ||
349 | } else if (value && options![value]) { | ||
350 | selection = options![value]; | ||
351 | } else { | ||
352 | selection = actionText; | ||
353 | } | ||
354 | |||
355 | return ( | ||
356 | <Wrapper className={className} identifier="franz-select"> | ||
357 | <Label | ||
358 | title={label} | ||
359 | showLabel={showLabel} | ||
360 | htmlFor={id} | ||
361 | className={classes.label} | ||
362 | isRequired={required} | ||
363 | > | ||
364 | <div | ||
365 | className={classnames({ | ||
366 | [`${classes.hasError}`]: error, | ||
367 | [`${classes.disabled}`]: disabled, | ||
368 | })} | ||
369 | ref={this.componentRef} | ||
370 | > | ||
371 | <button | ||
372 | type="button" | ||
373 | className={classnames({ | ||
374 | [`${inputClassName}`]: inputClassName, | ||
375 | [`${classes.select}`]: true, | ||
376 | [`${classes.hasError}`]: error, | ||
377 | })} | ||
378 | onClick={ | ||
379 | !disabled | ||
380 | ? () => | ||
381 | this.setState((state: IState) => ({ | ||
382 | open: !state.open, | ||
383 | })) | ||
384 | : () => {} | ||
385 | } | ||
386 | > | ||
387 | {selection} | ||
388 | <Icon | ||
389 | path={mdiArrowRightDropCircleOutline} | ||
390 | size={0.8} | ||
391 | className={classnames({ | ||
392 | [`${classes.toggle}`]: true, | ||
393 | [`${classes.toggleOpened}`]: open, | ||
394 | })} | ||
395 | /> | ||
396 | </button> | ||
397 | {showSearch && open && ( | ||
398 | <div className={classes.searchContainer}> | ||
399 | <Icon path={mdiMagnify} size={0.8} /> | ||
400 | <input | ||
401 | type="text" | ||
402 | value={needle} | ||
403 | onChange={e => this.setFilter(e.currentTarget.value)} | ||
404 | placeholder="Search" | ||
405 | className={classes.search} | ||
406 | ref={this.searchInputRef} | ||
407 | /> | ||
408 | {needle && ( | ||
409 | <button | ||
410 | type="button" | ||
411 | className={classes.clearNeedle} | ||
412 | onClick={() => this.setFilter()} | ||
413 | > | ||
414 | <Icon path={mdiCloseCircle} size={0.7} /> | ||
415 | </button> | ||
416 | )} | ||
417 | </div> | ||
418 | )} | ||
419 | <div | ||
420 | className={classnames({ | ||
421 | [`${classes.popup}`]: true, | ||
422 | [`${classes.open}`]: open, | ||
423 | })} | ||
424 | ref={this.scrollContainerRef} | ||
425 | > | ||
426 | {Object.keys(options!).map((key, i) => ( | ||
427 | <div | ||
428 | key={key} | ||
429 | onClick={() => this.select(key)} | ||
430 | className={classnames({ | ||
431 | [`${classes.option}`]: true, | ||
432 | [`${classes.selected}`]: options![key] === selection, | ||
433 | [`${classes.focused}`]: selected === i, | ||
434 | })} | ||
435 | onMouseOver={() => this.setState({ selected: i })} | ||
436 | ref={selected === i ? this.activeOptionRef : null} | ||
437 | > | ||
438 | {options![key]} | ||
439 | </div> | ||
440 | ))} | ||
441 | </div> | ||
442 | </div> | ||
443 | <input | ||
444 | className={classes.input} | ||
445 | id={id} | ||
446 | name={name} | ||
447 | type="hidden" | ||
448 | defaultValue={value} | ||
449 | onChange={onChange} | ||
450 | disabled={disabled} | ||
451 | ref={this.inputRef} | ||
452 | /> | ||
453 | </Label> | ||
454 | {error && <Error message={error} />} | ||
455 | </Wrapper> | ||
456 | ); | ||
457 | } | ||
458 | } | ||
459 | |||
460 | export const Select = injectStyle(styles)(SelectComponent); | ||
diff --git a/src/components/ui/textarea/index.tsx b/src/components/ui/textarea/index.tsx new file mode 100644 index 000000000..1b16698eb --- /dev/null +++ b/src/components/ui/textarea/index.tsx | |||
@@ -0,0 +1,126 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, createRef, TextareaHTMLAttributes } from 'react'; | ||
3 | import injectSheet from 'react-jss'; | ||
4 | |||
5 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
6 | |||
7 | import { Error } from '../error'; | ||
8 | import { Label } from '../label'; | ||
9 | import { Wrapper } from '../wrapper'; | ||
10 | |||
11 | import styles from './styles'; | ||
12 | |||
13 | interface IData { | ||
14 | [index: string]: string; | ||
15 | } | ||
16 | |||
17 | interface IProps | ||
18 | extends TextareaHTMLAttributes<HTMLTextAreaElement>, | ||
19 | IFormField, | ||
20 | IWithStyle { | ||
21 | focus?: boolean; | ||
22 | data: IData; | ||
23 | textareaClassName?: string; | ||
24 | } | ||
25 | |||
26 | class TextareaComponent extends Component<IProps> { | ||
27 | static defaultProps = { | ||
28 | focus: false, | ||
29 | onChange: () => {}, | ||
30 | onBlur: () => {}, | ||
31 | onFocus: () => {}, | ||
32 | showLabel: true, | ||
33 | disabled: false, | ||
34 | rows: 5, | ||
35 | }; | ||
36 | |||
37 | private textareaRef = createRef<HTMLTextAreaElement>(); | ||
38 | |||
39 | componentDidMount() { | ||
40 | const { data } = this.props; | ||
41 | |||
42 | if (this.textareaRef && this.textareaRef.current && data) { | ||
43 | Object.keys(data).map( | ||
44 | key => (this.textareaRef.current!.dataset[key] = data[key]), | ||
45 | ); | ||
46 | } | ||
47 | } | ||
48 | |||
49 | onChange(e: React.ChangeEvent<HTMLTextAreaElement>) { | ||
50 | const { onChange } = this.props; | ||
51 | |||
52 | if (onChange) { | ||
53 | onChange(e); | ||
54 | } | ||
55 | } | ||
56 | |||
57 | render() { | ||
58 | const { | ||
59 | classes, | ||
60 | className, | ||
61 | disabled, | ||
62 | error, | ||
63 | id, | ||
64 | textareaClassName, | ||
65 | label, | ||
66 | showLabel, | ||
67 | value, | ||
68 | name, | ||
69 | placeholder, | ||
70 | spellCheck, | ||
71 | onBlur, | ||
72 | onFocus, | ||
73 | minLength, | ||
74 | maxLength, | ||
75 | required, | ||
76 | rows, | ||
77 | noMargin, | ||
78 | } = this.props; | ||
79 | |||
80 | return ( | ||
81 | <Wrapper | ||
82 | className={className} | ||
83 | identifier="franz-textarea" | ||
84 | noMargin={noMargin} | ||
85 | > | ||
86 | <Label | ||
87 | title={label} | ||
88 | showLabel={showLabel} | ||
89 | htmlFor={id} | ||
90 | className={classes.label} | ||
91 | isRequired={required} | ||
92 | > | ||
93 | <div | ||
94 | className={classnames({ | ||
95 | [`${textareaClassName}`]: textareaClassName, | ||
96 | [`${classes.wrapper}`]: true, | ||
97 | [`${classes.disabled}`]: disabled, | ||
98 | [`${classes.hasError}`]: error, | ||
99 | })} | ||
100 | > | ||
101 | <textarea | ||
102 | id={id} | ||
103 | name={name} | ||
104 | placeholder={placeholder} | ||
105 | spellCheck={spellCheck} | ||
106 | className={classes.textarea} | ||
107 | ref={this.textareaRef} | ||
108 | onChange={this.onChange.bind(this)} | ||
109 | onFocus={onFocus} | ||
110 | onBlur={onBlur} | ||
111 | disabled={disabled} | ||
112 | minLength={minLength} | ||
113 | maxLength={maxLength} | ||
114 | rows={rows} | ||
115 | > | ||
116 | {value} | ||
117 | </textarea> | ||
118 | </div> | ||
119 | </Label> | ||
120 | {error && <Error message={error} />} | ||
121 | </Wrapper> | ||
122 | ); | ||
123 | } | ||
124 | } | ||
125 | |||
126 | export const Textarea = injectSheet(styles)(TextareaComponent); | ||
diff --git a/src/components/ui/textarea/styles.ts b/src/components/ui/textarea/styles.ts new file mode 100644 index 000000000..36fc2a82e --- /dev/null +++ b/src/components/ui/textarea/styles.ts | |||
@@ -0,0 +1,54 @@ | |||
1 | import { Property } from 'csstype'; | ||
2 | |||
3 | import { Theme } from '../../../themes'; | ||
4 | |||
5 | export default (theme: Theme) => ({ | ||
6 | label: { | ||
7 | '& > div': { | ||
8 | marginTop: 5, | ||
9 | }, | ||
10 | }, | ||
11 | disabled: { | ||
12 | opacity: theme.inputDisabledOpacity, | ||
13 | }, | ||
14 | formModifier: { | ||
15 | background: 'none', | ||
16 | border: 0, | ||
17 | borderLeft: theme.inputBorder, | ||
18 | padding: '4px 20px 0', | ||
19 | outline: 'none', | ||
20 | |||
21 | '&:active': { | ||
22 | opacity: 0.5, | ||
23 | }, | ||
24 | |||
25 | '& svg': { | ||
26 | fill: theme.inputModifierColor, | ||
27 | }, | ||
28 | }, | ||
29 | textarea: { | ||
30 | background: 'none', | ||
31 | border: 0, | ||
32 | fontSize: theme.uiFontSize, | ||
33 | outline: 'none', | ||
34 | padding: 8, | ||
35 | width: '100%', | ||
36 | color: theme.inputColor, | ||
37 | |||
38 | '&::placeholder': { | ||
39 | color: theme.inputPlaceholderColor, | ||
40 | }, | ||
41 | }, | ||
42 | wrapper: { | ||
43 | background: theme.inputBackground, | ||
44 | border: theme.inputBorder, | ||
45 | borderRadius: theme.borderRadiusSmall, | ||
46 | boxSizing: 'border-box' as Property.BoxSizing, | ||
47 | display: 'flex', | ||
48 | order: 1, | ||
49 | width: '100%', | ||
50 | }, | ||
51 | hasError: { | ||
52 | borderColor: theme.brandDanger, | ||
53 | }, | ||
54 | }); | ||
diff --git a/src/components/ui/toggle/index.tsx b/src/components/ui/toggle/index.tsx new file mode 100644 index 000000000..7b6ba147f --- /dev/null +++ b/src/components/ui/toggle/index.tsx | |||
@@ -0,0 +1,125 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Property } from 'csstype'; | ||
3 | import { Component, InputHTMLAttributes } from 'react'; | ||
4 | import injectStyle from 'react-jss'; | ||
5 | |||
6 | import { Theme } from '../../../themes'; | ||
7 | import { IFormField, IWithStyle } from '../typings/generic'; | ||
8 | |||
9 | import { Error } from '../error'; | ||
10 | import { Label } from '../label'; | ||
11 | import { Wrapper } from '../wrapper'; | ||
12 | |||
13 | interface IProps | ||
14 | extends InputHTMLAttributes<HTMLInputElement>, | ||
15 | IFormField, | ||
16 | IWithStyle { | ||
17 | className?: string; | ||
18 | } | ||
19 | |||
20 | let buttonTransition: string = 'none'; | ||
21 | |||
22 | if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { | ||
23 | buttonTransition = 'all .5s'; | ||
24 | } | ||
25 | |||
26 | const styles = (theme: Theme) => ({ | ||
27 | toggle: { | ||
28 | background: theme.toggleBackground, | ||
29 | borderRadius: theme.borderRadius, | ||
30 | height: theme.toggleHeight, | ||
31 | position: 'relative' as Property.Position, | ||
32 | width: theme.toggleWidth, | ||
33 | }, | ||
34 | button: { | ||
35 | background: theme.toggleButton, | ||
36 | borderRadius: '100%', | ||
37 | boxShadow: '0 1px 4px rgba(0, 0, 0, .3)', | ||
38 | width: theme.toggleHeight - 2, | ||
39 | height: theme.toggleHeight - 2, | ||
40 | left: 1, | ||
41 | top: 1, | ||
42 | position: 'absolute' as Property.Position, | ||
43 | transition: buttonTransition, | ||
44 | }, | ||
45 | buttonActive: { | ||
46 | background: theme.toggleButtonActive, | ||
47 | left: theme.toggleWidth - theme.toggleHeight + 1, | ||
48 | }, | ||
49 | input: { | ||
50 | visibility: 'hidden' as any, | ||
51 | }, | ||
52 | disabled: { | ||
53 | opacity: theme.inputDisabledOpacity, | ||
54 | }, | ||
55 | toggleLabel: { | ||
56 | display: 'flex', | ||
57 | alignItems: 'center', | ||
58 | |||
59 | '& > span': { | ||
60 | order: 1, | ||
61 | marginLeft: 15, | ||
62 | }, | ||
63 | }, | ||
64 | }); | ||
65 | |||
66 | class ToggleComponent extends Component<IProps> { | ||
67 | public static defaultProps = { | ||
68 | onChange: () => {}, | ||
69 | showLabel: true, | ||
70 | disabled: false, | ||
71 | error: '', | ||
72 | }; | ||
73 | |||
74 | render() { | ||
75 | const { | ||
76 | classes, | ||
77 | className, | ||
78 | disabled, | ||
79 | error, | ||
80 | id, | ||
81 | label, | ||
82 | showLabel, | ||
83 | checked, | ||
84 | value, | ||
85 | onChange, | ||
86 | } = this.props; | ||
87 | |||
88 | return ( | ||
89 | <Wrapper className={className} identifier="franz-toggle"> | ||
90 | <Label | ||
91 | title={label} | ||
92 | showLabel={showLabel} | ||
93 | htmlFor={id} | ||
94 | className={classes.toggleLabel} | ||
95 | > | ||
96 | <div | ||
97 | className={classnames({ | ||
98 | [`${classes.toggle}`]: true, | ||
99 | [`${classes.disabled}`]: disabled, | ||
100 | })} | ||
101 | > | ||
102 | <div | ||
103 | className={classnames({ | ||
104 | [`${classes.button}`]: true, | ||
105 | [`${classes.buttonActive}`]: checked, | ||
106 | })} | ||
107 | /> | ||
108 | <input | ||
109 | className={classes.input} | ||
110 | id={id} | ||
111 | type="checkbox" | ||
112 | checked={checked} | ||
113 | value={value} | ||
114 | onChange={onChange} | ||
115 | disabled={disabled} | ||
116 | /> | ||
117 | </div> | ||
118 | </Label> | ||
119 | {error && <Error message={error} />} | ||
120 | </Wrapper> | ||
121 | ); | ||
122 | } | ||
123 | } | ||
124 | |||
125 | export const Toggle = injectStyle(styles)(ToggleComponent); | ||
diff --git a/src/components/ui/typings/generic.ts b/src/components/ui/typings/generic.ts new file mode 100644 index 000000000..65b996d59 --- /dev/null +++ b/src/components/ui/typings/generic.ts | |||
@@ -0,0 +1,19 @@ | |||
1 | import { Classes } from 'jss'; | ||
2 | |||
3 | import { Theme } from '../../../themes'; | ||
4 | |||
5 | export interface IFormField { | ||
6 | showLabel?: boolean; | ||
7 | label?: string; | ||
8 | error?: string; | ||
9 | required?: boolean; | ||
10 | noMargin?: boolean; | ||
11 | } | ||
12 | |||
13 | export interface IWithStyle { | ||
14 | classes: Classes; | ||
15 | theme: Theme; | ||
16 | } | ||
17 | |||
18 | export type Merge<M, N> = Omit<M, Extract<keyof M, keyof N>> & N; | ||
19 | export type Omit<T, K extends keyof T> = Pick<T, Exclude<keyof T, K>>; | ||
diff --git a/src/components/ui/wrapper/index.tsx b/src/components/ui/wrapper/index.tsx new file mode 100644 index 000000000..ffcd6fe0b --- /dev/null +++ b/src/components/ui/wrapper/index.tsx | |||
@@ -0,0 +1,37 @@ | |||
1 | import classnames from 'classnames'; | ||
2 | import { Component, ReactNode } from 'react'; | ||
3 | import injectStyle from 'react-jss'; | ||
4 | import { IWithStyle } from '../typings/generic'; | ||
5 | |||
6 | interface IProps extends IWithStyle { | ||
7 | children: ReactNode; | ||
8 | className?: string; | ||
9 | identifier: string; | ||
10 | noMargin?: boolean; | ||
11 | } | ||
12 | |||
13 | const styles = { | ||
14 | container: { | ||
15 | marginBottom: (props: IProps) => (props.noMargin ? 0 : 20), | ||
16 | }, | ||
17 | }; | ||
18 | |||
19 | class WrapperComponent extends Component<IProps> { | ||
20 | render() { | ||
21 | const { children, classes, className, identifier } = this.props; | ||
22 | |||
23 | return ( | ||
24 | <div | ||
25 | className={classnames({ | ||
26 | [`${classes.container}`]: true, | ||
27 | [`${className}`]: className, | ||
28 | })} | ||
29 | data-type={identifier} | ||
30 | > | ||
31 | {children} | ||
32 | </div> | ||
33 | ); | ||
34 | } | ||
35 | } | ||
36 | |||
37 | export const Wrapper = injectStyle(styles)(WrapperComponent); | ||
diff --git a/src/components/util/ErrorBoundary/index.js b/src/components/util/ErrorBoundary/index.js index 9c789e981..cddcd91c2 100644 --- a/src/components/util/ErrorBoundary/index.js +++ b/src/components/util/ErrorBoundary/index.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import injectSheet from 'react-jss'; | 3 | import injectSheet from 'react-jss'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/components/util/ErrorBoundary/styles.js b/src/components/util/ErrorBoundary/styles.js index 51b36fdf3..0960546ff 100644 --- a/src/components/util/ErrorBoundary/styles.js +++ b/src/components/util/ErrorBoundary/styles.js | |||
@@ -1,4 +1,4 @@ | |||
1 | export default (theme) => ({ | 1 | export default theme => ({ |
2 | component: { | 2 | component: { |
3 | display: 'flex', | 3 | display: 'flex', |
4 | width: '100%', | 4 | width: '100%', |
diff --git a/src/config.ts b/src/config.ts index fe9145021..dfedbe6f5 100644 --- a/src/config.ts +++ b/src/config.ts | |||
@@ -2,7 +2,7 @@ | |||
2 | 2 | ||
3 | import ms from 'ms'; | 3 | import ms from 'ms'; |
4 | 4 | ||
5 | import { DEFAULT_ACCENT_COLOR } from '@meetfranz/theme'; | 5 | export const DEFAULT_ACCENT_COLOR = '#7266F0'; |
6 | 6 | ||
7 | export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks | 7 | export const CHECK_INTERVAL = ms('1h'); // How often should we perform checks |
8 | 8 | ||
@@ -180,6 +180,7 @@ export const CUSTOM_WEBSITE_RECIPE_ID = 'franz-custom-website'; | |||
180 | export const DEFAULT_SERVICE_ORDER = 99; // something high enough that it gets added to the end of the already-added services on the left sidebar | 180 | export const DEFAULT_SERVICE_ORDER = 99; // something high enough that it gets added to the end of the already-added services on the left sidebar |
181 | 181 | ||
182 | export const DEFAULT_APP_SETTINGS = { | 182 | export const DEFAULT_APP_SETTINGS = { |
183 | autoLaunchOnStart: false, | ||
183 | autoLaunchInBackground: false, | 184 | autoLaunchInBackground: false, |
184 | runInBackground: true, | 185 | runInBackground: true, |
185 | reloadAfterResume: true, | 186 | reloadAfterResume: true, |
@@ -198,11 +199,11 @@ export const DEFAULT_APP_SETTINGS = { | |||
198 | spellcheckerLanguage: 'en-us', | 199 | spellcheckerLanguage: 'en-us', |
199 | darkMode: false, | 200 | darkMode: false, |
200 | splitMode: false, | 201 | splitMode: false, |
201 | locale: '', | ||
202 | fallbackLocale: 'en-US', | 202 | fallbackLocale: 'en-US', |
203 | beta: false, | 203 | beta: false, |
204 | isAppMuted: false, | 204 | isAppMuted: false, |
205 | enableGPUAcceleration: true, | 205 | enableGPUAcceleration: true, |
206 | enableGlobalHideShortcut: false, | ||
206 | 207 | ||
207 | // Ferdi specific options | 208 | // Ferdi specific options |
208 | server: LIVE_FERDI_API, | 209 | server: LIVE_FERDI_API, |
@@ -233,4 +234,28 @@ export const DEFAULT_APP_SETTINGS = { | |||
233 | useVerticalStyle: false, | 234 | useVerticalStyle: false, |
234 | alwaysShowWorkspaces: false, | 235 | alwaysShowWorkspaces: false, |
235 | liftSingleInstanceLock: false, | 236 | liftSingleInstanceLock: false, |
237 | enableLongPressServiceHint: false, | ||
238 | proxyFeatureEnabled: false, | ||
239 | onlyShowFavoritesInUnreadCount: false | ||
240 | }; | ||
241 | |||
242 | export const DEFAULT_SERVICE_SETTINGS = { | ||
243 | isEnabled: true, | ||
244 | isHibernationEnabled: false, | ||
245 | isWakeUpEnabled: true, | ||
246 | isNotificationEnabled: true, | ||
247 | isBadgeEnabled: true, | ||
248 | isMuted: false, | ||
249 | customIcon: false, | ||
250 | isDarkModeEnabled: false, | ||
251 | // Note: Do NOT change these default values. If they change, then the corresponding changes in the recipes needs to be done | ||
252 | hasDirectMessages: true, | ||
253 | hasIndirectMessages: false, | ||
254 | hasNotificationSound: false, | ||
255 | hasTeamId: false, | ||
256 | hasCustomUrl: false, | ||
257 | hasHostedOption: false, | ||
258 | allowFavoritesDelineationInUnreadCount: false, | ||
259 | disablewebsecurity: false, | ||
260 | autoHibernate: false, | ||
236 | }; | 261 | }; |
diff --git a/src/containers/auth/AuthLayoutContainer.js b/src/containers/auth/AuthLayoutContainer.js index 7673ab9c7..aa36e3969 100644 --- a/src/containers/auth/AuthLayoutContainer.js +++ b/src/containers/auth/AuthLayoutContainer.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { ThemeProvider } from 'react-jss'; | 4 | import { ThemeProvider } from 'react-jss'; |
diff --git a/src/containers/auth/ChangeServerScreen.js b/src/containers/auth/ChangeServerScreen.js index a8910e7b1..dcc913c39 100644 --- a/src/containers/auth/ChangeServerScreen.js +++ b/src/containers/auth/ChangeServerScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { RouterStore } from 'mobx-react-router'; | 4 | import { RouterStore } from 'mobx-react-router'; |
diff --git a/src/containers/auth/ImportScreen.js b/src/containers/auth/ImportScreen.js index ce786bdb8..46e2d41f0 100644 --- a/src/containers/auth/ImportScreen.js +++ b/src/containers/auth/ImportScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { RouterStore } from 'mobx-react-router'; | 4 | import { RouterStore } from 'mobx-react-router'; |
diff --git a/src/containers/auth/InviteScreen.js b/src/containers/auth/InviteScreen.js index d9d52a57c..e252242ae 100644 --- a/src/containers/auth/InviteScreen.js +++ b/src/containers/auth/InviteScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import Invite from '../../components/auth/Invite'; | 4 | import Invite from '../../components/auth/Invite'; |
diff --git a/src/containers/auth/LockedScreen.js b/src/containers/auth/LockedScreen.js index a49549731..945e41284 100644 --- a/src/containers/auth/LockedScreen.js +++ b/src/containers/auth/LockedScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import Locked from '../../components/auth/Locked'; | 4 | import Locked from '../../components/auth/Locked'; |
diff --git a/src/containers/auth/LoginScreen.js b/src/containers/auth/LoginScreen.js index cab73316b..3f8c67fa8 100644 --- a/src/containers/auth/LoginScreen.js +++ b/src/containers/auth/LoginScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import Login from '../../components/auth/Login'; | 4 | import Login from '../../components/auth/Login'; |
diff --git a/src/containers/auth/PasswordScreen.js b/src/containers/auth/PasswordScreen.js index 86a746b9b..836d4cc88 100644 --- a/src/containers/auth/PasswordScreen.js +++ b/src/containers/auth/PasswordScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import Password from '../../components/auth/Password'; | 4 | import Password from '../../components/auth/Password'; |
diff --git a/src/containers/auth/SetupAssistantScreen.js b/src/containers/auth/SetupAssistantScreen.js index efda2e9d2..8cdd95a88 100644 --- a/src/containers/auth/SetupAssistantScreen.js +++ b/src/containers/auth/SetupAssistantScreen.js | |||
@@ -1,5 +1,5 @@ | |||
1 | /* eslint-disable no-await-in-loop */ | 1 | /* eslint-disable no-await-in-loop */ |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { inject, observer } from 'mobx-react'; | 4 | import { inject, observer } from 'mobx-react'; |
5 | 5 | ||
diff --git a/src/containers/auth/SignupScreen.js b/src/containers/auth/SignupScreen.js index b20a7fd62..3b19f3c50 100644 --- a/src/containers/auth/SignupScreen.js +++ b/src/containers/auth/SignupScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/containers/auth/WelcomeScreen.js b/src/containers/auth/WelcomeScreen.js index d169e5f0f..7a23d9ba9 100644 --- a/src/containers/auth/WelcomeScreen.js +++ b/src/containers/auth/WelcomeScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/containers/layout/AppLayoutContainer.js b/src/containers/layout/AppLayoutContainer.js index 3e92b0610..0d566525d 100644 --- a/src/containers/layout/AppLayoutContainer.js +++ b/src/containers/layout/AppLayoutContainer.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Children, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { ThemeProvider } from 'react-jss'; | 4 | import { ThemeProvider } from 'react-jss'; |
@@ -8,7 +8,6 @@ import RecipesStore from '../../stores/RecipesStore'; | |||
8 | import ServicesStore from '../../stores/ServicesStore'; | 8 | import ServicesStore from '../../stores/ServicesStore'; |
9 | import FeaturesStore from '../../stores/FeaturesStore'; | 9 | import FeaturesStore from '../../stores/FeaturesStore'; |
10 | import UIStore from '../../stores/UIStore'; | 10 | import UIStore from '../../stores/UIStore'; |
11 | import NewsStore from '../../stores/NewsStore'; | ||
12 | import SettingsStore from '../../stores/SettingsStore'; | 11 | import SettingsStore from '../../stores/SettingsStore'; |
13 | import UserStore from '../../stores/UserStore'; | 12 | import UserStore from '../../stores/UserStore'; |
14 | import RequestStore from '../../stores/RequestStore'; | 13 | import RequestStore from '../../stores/RequestStore'; |
@@ -38,7 +37,6 @@ class AppLayoutContainer extends Component { | |||
38 | features, | 37 | features, |
39 | services, | 38 | services, |
40 | ui, | 39 | ui, |
41 | news, | ||
42 | settings, | 40 | settings, |
43 | globalError, | 41 | globalError, |
44 | requests, | 42 | requests, |
@@ -63,8 +61,6 @@ class AppLayoutContainer extends Component { | |||
63 | awake, | 61 | awake, |
64 | } = this.props.actions.service; | 62 | } = this.props.actions.service; |
65 | 63 | ||
66 | const { hide } = this.props.actions.news; | ||
67 | |||
68 | const { retryRequiredRequests } = this.props.actions.requests; | 64 | const { retryRequiredRequests } = this.props.actions.requests; |
69 | 65 | ||
70 | const { installUpdate, toggleMuteApp } = this.props.actions.app; | 66 | const { installUpdate, toggleMuteApp } = this.props.actions.app; |
@@ -95,10 +91,11 @@ class AppLayoutContainer extends Component { | |||
95 | 91 | ||
96 | const workspacesDrawer = ( | 92 | const workspacesDrawer = ( |
97 | <WorkspaceDrawer | 93 | <WorkspaceDrawer |
94 | // eslint-disable-next-line no-confusing-arrow | ||
98 | getServicesForWorkspace={workspace => | 95 | getServicesForWorkspace={workspace => |
99 | (workspace | 96 | workspace |
100 | ? workspaceStore.getWorkspaceServices(workspace).map(s => s.name) | 97 | ? workspaceStore.getWorkspaceServices(workspace).map(s => s.name) |
101 | : services.all.map(s => s.name)) | 98 | : services.all.map(s => s.name) |
102 | } | 99 | } |
103 | /> | 100 | /> |
104 | ); | 101 | ); |
@@ -158,8 +155,6 @@ class AppLayoutContainer extends Component { | |||
158 | sidebar={sidebar} | 155 | sidebar={sidebar} |
159 | workspacesDrawer={workspacesDrawer} | 156 | workspacesDrawer={workspacesDrawer} |
160 | services={servicesContainer} | 157 | services={servicesContainer} |
161 | news={news.latest} | ||
162 | removeNewsItem={hide} | ||
163 | reloadServicesAfterUpdate={() => window.location.reload()} | 158 | reloadServicesAfterUpdate={() => window.location.reload()} |
164 | installAppUpdate={installUpdate} | 159 | installAppUpdate={installUpdate} |
165 | globalError={globalError.error} | 160 | globalError={globalError.error} |
@@ -168,7 +163,7 @@ class AppLayoutContainer extends Component { | |||
168 | retryRequiredRequests={retryRequiredRequests} | 163 | retryRequiredRequests={retryRequiredRequests} |
169 | areRequiredRequestsLoading={requests.areRequiredRequestsLoading} | 164 | areRequiredRequestsLoading={requests.areRequiredRequestsLoading} |
170 | > | 165 | > |
171 | {React.Children.count(children) > 0 ? children : null} | 166 | {Children.count(children) > 0 ? children : null} |
172 | </AppLayout> | 167 | </AppLayout> |
173 | </ThemeProvider> | 168 | </ThemeProvider> |
174 | ); | 169 | ); |
@@ -182,7 +177,6 @@ AppLayoutContainer.wrappedComponent.propTypes = { | |||
182 | recipes: PropTypes.instanceOf(RecipesStore).isRequired, | 177 | recipes: PropTypes.instanceOf(RecipesStore).isRequired, |
183 | app: PropTypes.instanceOf(AppStore).isRequired, | 178 | app: PropTypes.instanceOf(AppStore).isRequired, |
184 | ui: PropTypes.instanceOf(UIStore).isRequired, | 179 | ui: PropTypes.instanceOf(UIStore).isRequired, |
185 | news: PropTypes.instanceOf(NewsStore).isRequired, | ||
186 | settings: PropTypes.instanceOf(SettingsStore).isRequired, | 180 | settings: PropTypes.instanceOf(SettingsStore).isRequired, |
187 | user: PropTypes.instanceOf(UserStore).isRequired, | 181 | user: PropTypes.instanceOf(UserStore).isRequired, |
188 | requests: PropTypes.instanceOf(RequestStore).isRequired, | 182 | requests: PropTypes.instanceOf(RequestStore).isRequired, |
@@ -191,7 +185,6 @@ AppLayoutContainer.wrappedComponent.propTypes = { | |||
191 | }).isRequired, | 185 | }).isRequired, |
192 | actions: PropTypes.shape({ | 186 | actions: PropTypes.shape({ |
193 | service: PropTypes.instanceOf(ServicesStore).isRequired, | 187 | service: PropTypes.instanceOf(ServicesStore).isRequired, |
194 | news: PropTypes.instanceOf(NewsStore).isRequired, | ||
195 | ui: PropTypes.instanceOf(UIStore).isRequired, | 188 | ui: PropTypes.instanceOf(UIStore).isRequired, |
196 | app: PropTypes.instanceOf(AppStore).isRequired, | 189 | app: PropTypes.instanceOf(AppStore).isRequired, |
197 | requests: PropTypes.instanceOf(RequestStore).isRequired, | 190 | requests: PropTypes.instanceOf(RequestStore).isRequired, |
diff --git a/src/containers/settings/AccountScreen.js b/src/containers/settings/AccountScreen.js index 1515fc22b..f7c9b8164 100644 --- a/src/containers/settings/AccountScreen.js +++ b/src/containers/settings/AccountScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/containers/settings/EditServiceScreen.js b/src/containers/settings/EditServiceScreen.js index dee7e7cff..b84c0d5bb 100644 --- a/src/containers/settings/EditServiceScreen.js +++ b/src/containers/settings/EditServiceScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
@@ -23,6 +23,7 @@ import { config as proxyFeature } from '../../features/serviceProxy'; | |||
23 | import { SPELLCHECKER_LOCALES } from '../../i18n/languages'; | 23 | import { SPELLCHECKER_LOCALES } from '../../i18n/languages'; |
24 | 24 | ||
25 | import globalMessages from '../../i18n/globalMessages'; | 25 | import globalMessages from '../../i18n/globalMessages'; |
26 | import { DEFAULT_APP_SETTINGS, DEFAULT_SERVICE_SETTINGS } from '../../config'; | ||
26 | 27 | ||
27 | const messages = defineMessages({ | 28 | const messages = defineMessages({ |
28 | name: { | 29 | name: { |
@@ -37,6 +38,10 @@ const messages = defineMessages({ | |||
37 | id: 'settings.service.form.enableHibernation', | 38 | id: 'settings.service.form.enableHibernation', |
38 | defaultMessage: 'Enable hibernation', | 39 | defaultMessage: 'Enable hibernation', |
39 | }, | 40 | }, |
41 | enableWakeUp: { | ||
42 | id: 'settings.service.form.enableWakeUp', | ||
43 | defaultMessage: 'Enable wake up', | ||
44 | }, | ||
40 | enableNotification: { | 45 | enableNotification: { |
41 | id: 'settings.service.form.enableNotification', | 46 | id: 'settings.service.form.enableNotification', |
42 | defaultMessage: 'Enable notifications', | 47 | defaultMessage: 'Enable notifications', |
@@ -171,7 +176,7 @@ class EditServiceScreen extends Component { | |||
171 | isEnabled: { | 176 | isEnabled: { |
172 | label: intl.formatMessage(messages.enableService), | 177 | label: intl.formatMessage(messages.enableService), |
173 | value: service.isEnabled, | 178 | value: service.isEnabled, |
174 | default: true, | 179 | default: DEFAULT_SERVICE_SETTINGS.isEnabled, |
175 | }, | 180 | }, |
176 | isHibernationEnabled: { | 181 | isHibernationEnabled: { |
177 | label: intl.formatMessage(messages.enableHibernation), | 182 | label: intl.formatMessage(messages.enableHibernation), |
@@ -179,22 +184,27 @@ class EditServiceScreen extends Component { | |||
179 | action !== 'edit' | 184 | action !== 'edit' |
180 | ? recipe.autoHibernate | 185 | ? recipe.autoHibernate |
181 | : service.isHibernationEnabled, | 186 | : service.isHibernationEnabled, |
182 | default: true, | 187 | default: DEFAULT_SERVICE_SETTINGS.isHibernationEnabled, |
188 | }, | ||
189 | isWakeUpEnabled: { | ||
190 | label: intl.formatMessage(messages.enableWakeUp), | ||
191 | value: service.isWakeUpEnabled, | ||
192 | default: DEFAULT_SERVICE_SETTINGS.isWakeUpEnabled, | ||
183 | }, | 193 | }, |
184 | isNotificationEnabled: { | 194 | isNotificationEnabled: { |
185 | label: intl.formatMessage(messages.enableNotification), | 195 | label: intl.formatMessage(messages.enableNotification), |
186 | value: service.isNotificationEnabled, | 196 | value: service.isNotificationEnabled, |
187 | default: true, | 197 | default: DEFAULT_SERVICE_SETTINGS.isNotificationEnabled, |
188 | }, | 198 | }, |
189 | isBadgeEnabled: { | 199 | isBadgeEnabled: { |
190 | label: intl.formatMessage(messages.enableBadge), | 200 | label: intl.formatMessage(messages.enableBadge), |
191 | value: service.isBadgeEnabled, | 201 | value: service.isBadgeEnabled, |
192 | default: true, | 202 | default: DEFAULT_SERVICE_SETTINGS.isBadgeEnabled, |
193 | }, | 203 | }, |
194 | isMuted: { | 204 | isMuted: { |
195 | label: intl.formatMessage(messages.enableAudio), | 205 | label: intl.formatMessage(messages.enableAudio), |
196 | value: !service.isMuted, | 206 | value: !service.isMuted, |
197 | default: true, | 207 | default: DEFAULT_SERVICE_SETTINGS.isMuted, |
198 | }, | 208 | }, |
199 | customIcon: { | 209 | customIcon: { |
200 | label: intl.formatMessage(messages.icon), | 210 | label: intl.formatMessage(messages.icon), |
@@ -288,7 +298,7 @@ class EditServiceScreen extends Component { | |||
288 | isIndirectMessageBadgeEnabled: { | 298 | isIndirectMessageBadgeEnabled: { |
289 | label: intl.formatMessage(messages.indirectMessages), | 299 | label: intl.formatMessage(messages.indirectMessages), |
290 | value: service.isIndirectMessageBadgeEnabled, | 300 | value: service.isIndirectMessageBadgeEnabled, |
291 | default: true, | 301 | default: DEFAULT_SERVICE_SETTINGS.hasIndirectMessages, |
292 | }, | 302 | }, |
293 | }); | 303 | }); |
294 | } | 304 | } |
@@ -298,7 +308,7 @@ class EditServiceScreen extends Component { | |||
298 | onlyShowFavoritesInUnreadCount: { | 308 | onlyShowFavoritesInUnreadCount: { |
299 | label: intl.formatMessage(messages.onlyShowFavoritesInUnreadCount), | 309 | label: intl.formatMessage(messages.onlyShowFavoritesInUnreadCount), |
300 | value: service.onlyShowFavoritesInUnreadCount, | 310 | value: service.onlyShowFavoritesInUnreadCount, |
301 | default: false, | 311 | default: DEFAULT_APP_SETTINGS.onlyShowFavoritesInUnreadCount, |
302 | }, | 312 | }, |
303 | }); | 313 | }); |
304 | } | 314 | } |
@@ -314,7 +324,7 @@ class EditServiceScreen extends Component { | |||
314 | isEnabled: { | 324 | isEnabled: { |
315 | label: intl.formatMessage(messages.enableProxy), | 325 | label: intl.formatMessage(messages.enableProxy), |
316 | value: serviceProxyConfig.isEnabled, | 326 | value: serviceProxyConfig.isEnabled, |
317 | default: false, | 327 | default: DEFAULT_APP_SETTINGS.proxyFeatureEnabled, |
318 | }, | 328 | }, |
319 | host: { | 329 | host: { |
320 | label: intl.formatMessage(messages.proxyHost), | 330 | label: intl.formatMessage(messages.proxyHost), |
diff --git a/src/containers/settings/EditSettingsScreen.js b/src/containers/settings/EditSettingsScreen.js index 1b05644f9..de0714870 100644 --- a/src/containers/settings/EditSettingsScreen.js +++ b/src/containers/settings/EditSettingsScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
@@ -177,6 +177,10 @@ const messages = defineMessages({ | |||
177 | id: 'settings.app.form.iconSize', | 177 | id: 'settings.app.form.iconSize', |
178 | defaultMessage: 'Service icon size', | 178 | defaultMessage: 'Service icon size', |
179 | }, | 179 | }, |
180 | enableLongPressServiceHint: { | ||
181 | id: 'settings.app.form.enableLongPressServiceHint', | ||
182 | defaultMessage: 'Enable service shortcut hint on long press', | ||
183 | }, | ||
180 | useVerticalStyle: { | 184 | useVerticalStyle: { |
181 | id: 'settings.app.form.useVerticalStyle', | 185 | id: 'settings.app.form.useVerticalStyle', |
182 | defaultMessage: 'Use horizontal style', | 186 | defaultMessage: 'Use horizontal style', |
@@ -209,6 +213,10 @@ const messages = defineMessages({ | |||
209 | id: 'settings.app.form.enableGPUAcceleration', | 213 | id: 'settings.app.form.enableGPUAcceleration', |
210 | defaultMessage: 'Enable GPU Acceleration', | 214 | defaultMessage: 'Enable GPU Acceleration', |
211 | }, | 215 | }, |
216 | enableGlobalHideShortcut: { | ||
217 | id: 'settings.app.form.enableGlobalHideShortcut', | ||
218 | defaultMessage: 'Enable Global shortcut to hide Ferdi', | ||
219 | }, | ||
212 | beta: { | 220 | beta: { |
213 | id: 'settings.app.form.beta', | 221 | id: 'settings.app.form.beta', |
214 | defaultMessage: 'Include beta versions', | 222 | defaultMessage: 'Include beta versions', |
@@ -289,6 +297,9 @@ class EditSettingsScreen extends Component { | |||
289 | scheduledDNDStart: settingsData.scheduledDNDStart, | 297 | scheduledDNDStart: settingsData.scheduledDNDStart, |
290 | scheduledDNDEnd: settingsData.scheduledDNDEnd, | 298 | scheduledDNDEnd: settingsData.scheduledDNDEnd, |
291 | enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration), | 299 | enableGPUAcceleration: Boolean(settingsData.enableGPUAcceleration), |
300 | enableGlobalHideShortcut: Boolean( | ||
301 | settingsData.enableGlobalHideShortcut, | ||
302 | ), | ||
292 | showDisabledServices: Boolean(settingsData.showDisabledServices), | 303 | showDisabledServices: Boolean(settingsData.showDisabledServices), |
293 | darkMode: Boolean(settingsData.darkMode), | 304 | darkMode: Boolean(settingsData.darkMode), |
294 | adaptableDarkMode: Boolean(settingsData.adaptableDarkMode), | 305 | adaptableDarkMode: Boolean(settingsData.adaptableDarkMode), |
@@ -296,6 +307,9 @@ class EditSettingsScreen extends Component { | |||
296 | splitMode: Boolean(settingsData.splitMode), | 307 | splitMode: Boolean(settingsData.splitMode), |
297 | serviceRibbonWidth: Number(settingsData.serviceRibbonWidth), | 308 | serviceRibbonWidth: Number(settingsData.serviceRibbonWidth), |
298 | iconSize: Number(settingsData.iconSize), | 309 | iconSize: Number(settingsData.iconSize), |
310 | enableLongPressServiceHint: Boolean( | ||
311 | settingsData.enableLongPressServiceHint, | ||
312 | ), | ||
299 | useVerticalStyle: Boolean(settingsData.useVerticalStyle), | 313 | useVerticalStyle: Boolean(settingsData.useVerticalStyle), |
300 | alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces), | 314 | alwaysShowWorkspaces: Boolean(settingsData.alwaysShowWorkspaces), |
301 | accentColor: settingsData.accentColor, | 315 | accentColor: settingsData.accentColor, |
@@ -503,7 +517,7 @@ class EditSettingsScreen extends Component { | |||
503 | lockingFeatureEnabled: { | 517 | lockingFeatureEnabled: { |
504 | label: intl.formatMessage(messages.enableLock), | 518 | label: intl.formatMessage(messages.enableLock), |
505 | value: settings.all.app.lockingFeatureEnabled || false, | 519 | value: settings.all.app.lockingFeatureEnabled || false, |
506 | default: false, | 520 | default: DEFAULT_APP_SETTINGS.lockingFeatureEnabled, |
507 | }, | 521 | }, |
508 | lockedPassword: { | 522 | lockedPassword: { |
509 | label: intl.formatMessage(messages.lockPassword), | 523 | label: intl.formatMessage(messages.lockPassword), |
@@ -525,7 +539,7 @@ class EditSettingsScreen extends Component { | |||
525 | scheduledDNDEnabled: { | 539 | scheduledDNDEnabled: { |
526 | label: intl.formatMessage(messages.scheduledDNDEnabled), | 540 | label: intl.formatMessage(messages.scheduledDNDEnabled), |
527 | value: settings.all.app.scheduledDNDEnabled || false, | 541 | value: settings.all.app.scheduledDNDEnabled || false, |
528 | default: false, | 542 | default: DEFAULT_APP_SETTINGS.scheduledDNDEnabled, |
529 | }, | 543 | }, |
530 | scheduledDNDStart: { | 544 | scheduledDNDStart: { |
531 | label: intl.formatMessage(messages.scheduledDNDStart), | 545 | label: intl.formatMessage(messages.scheduledDNDStart), |
@@ -603,6 +617,11 @@ class EditSettingsScreen extends Component { | |||
603 | default: DEFAULT_APP_SETTINGS.iconSize, | 617 | default: DEFAULT_APP_SETTINGS.iconSize, |
604 | options: iconSizes, | 618 | options: iconSizes, |
605 | }, | 619 | }, |
620 | enableLongPressServiceHint: { | ||
621 | label: intl.formatMessage(messages.enableLongPressServiceHint), | ||
622 | value: settings.all.app.enableLongPressServiceHint, | ||
623 | default: DEFAULT_APP_SETTINGS.enableLongPressServiceHint, | ||
624 | }, | ||
606 | useVerticalStyle: { | 625 | useVerticalStyle: { |
607 | label: intl.formatMessage(messages.useVerticalStyle), | 626 | label: intl.formatMessage(messages.useVerticalStyle), |
608 | value: settings.all.app.useVerticalStyle, | 627 | value: settings.all.app.useVerticalStyle, |
@@ -623,6 +642,11 @@ class EditSettingsScreen extends Component { | |||
623 | value: settings.all.app.enableGPUAcceleration, | 642 | value: settings.all.app.enableGPUAcceleration, |
624 | default: DEFAULT_APP_SETTINGS.enableGPUAcceleration, | 643 | default: DEFAULT_APP_SETTINGS.enableGPUAcceleration, |
625 | }, | 644 | }, |
645 | enableGlobalHideShortcut: { | ||
646 | label: intl.formatMessage(messages.enableGlobalHideShortcut), | ||
647 | value: settings.all.app.enableGlobalHideShortcut, | ||
648 | default: DEFAULT_APP_SETTINGS.enableGlobalHideShortcut, | ||
649 | }, | ||
626 | locale: { | 650 | locale: { |
627 | label: intl.formatMessage(messages.language), | 651 | label: intl.formatMessage(messages.language), |
628 | value: app.locale, | 652 | value: app.locale, |
diff --git a/src/containers/settings/EditUserScreen.js b/src/containers/settings/EditUserScreen.js index ca1363c59..1dc0aa8e6 100644 --- a/src/containers/settings/EditUserScreen.js +++ b/src/containers/settings/EditUserScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
diff --git a/src/containers/settings/InviteScreen.js b/src/containers/settings/InviteScreen.js index bf393f42f..592b4b11c 100644 --- a/src/containers/settings/InviteScreen.js +++ b/src/containers/settings/InviteScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/containers/settings/RecipesScreen.js b/src/containers/settings/RecipesScreen.js index 2e60ceefa..7f55e54c5 100644 --- a/src/containers/settings/RecipesScreen.js +++ b/src/containers/settings/RecipesScreen.js | |||
@@ -1,5 +1,5 @@ | |||
1 | import { readJsonSync } from 'fs-extra'; | 1 | import { readJsonSync } from 'fs-extra'; |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { autorun } from 'mobx'; | 4 | import { autorun } from 'mobx'; |
5 | import { inject, observer } from 'mobx-react'; | 5 | import { inject, observer } from 'mobx-react'; |
@@ -130,21 +130,21 @@ class RecipesScreen extends Component { | |||
130 | 130 | ||
131 | const allRecipes = this.state.needle | 131 | const allRecipes = this.state.needle |
132 | ? this.prepareRecipes([ | 132 | ? this.prepareRecipes([ |
133 | // All search recipes from server | 133 | // All search recipes from server |
134 | ...recipePreviews.searchResults, | 134 | ...recipePreviews.searchResults, |
135 | // All search recipes from local recipes | 135 | // All search recipes from local recipes |
136 | ...this.createPreviews( | 136 | ...this.createPreviews( |
137 | this.customRecipes.filter( | 137 | this.customRecipes.filter( |
138 | service => | 138 | service => |
139 | service.name | 139 | service.name |
140 | .toLowerCase() | 140 | .toLowerCase() |
141 | .includes(this.state.needle.toLowerCase()) || | 141 | .includes(this.state.needle.toLowerCase()) || |
142 | (service.aliases || []).some(alias => | 142 | (service.aliases || []).some(alias => |
143 | alias.toLowerCase().includes(this.state.needle.toLowerCase()), | 143 | alias.toLowerCase().includes(this.state.needle.toLowerCase()), |
144 | ), | 144 | ), |
145 | ), | ||
145 | ), | 146 | ), |
146 | ), | 147 | ]).sort(this._sortByName) |
147 | ]).sort(this._sortByName) | ||
148 | : recipeFilter; | 148 | : recipeFilter; |
149 | 149 | ||
150 | const customWebsiteRecipe = recipePreviews.all.find( | 150 | const customWebsiteRecipe = recipePreviews.all.find( |
diff --git a/src/containers/settings/ServicesScreen.js b/src/containers/settings/ServicesScreen.js index c9dfc68d0..a657b6e6c 100644 --- a/src/containers/settings/ServicesScreen.js +++ b/src/containers/settings/ServicesScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { RouterStore } from 'mobx-react-router'; | 4 | import { RouterStore } from 'mobx-react-router'; |
diff --git a/src/containers/settings/SettingsWindow.js b/src/containers/settings/SettingsWindow.js index e03c4c1d2..35db3a434 100644 --- a/src/containers/settings/SettingsWindow.js +++ b/src/containers/settings/SettingsWindow.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import ReactDOM from 'react-dom'; | 2 | import ReactDOM from 'react-dom'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
diff --git a/src/containers/settings/SupportScreen.js b/src/containers/settings/SupportScreen.js index 646f672ce..d3600f8ea 100644 --- a/src/containers/settings/SupportScreen.js +++ b/src/containers/settings/SupportScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { inject } from 'mobx-react'; | 2 | import { inject } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | 4 | ||
diff --git a/src/containers/settings/TeamScreen.js b/src/containers/settings/TeamScreen.js index ea447469b..928262a59 100644 --- a/src/containers/settings/TeamScreen.js +++ b/src/containers/settings/TeamScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | 4 | ||
diff --git a/src/electron-util.ts b/src/electron-util.ts index f4b26cb10..3c395ab10 100644 --- a/src/electron-util.ts +++ b/src/electron-util.ts | |||
@@ -1,23 +1,28 @@ | |||
1 | // Enhanced from: https://github.com/dertieran/electron-util/blob/replace-remote/source/api.js | 1 | // Enhanced from: https://github.com/dertieran/electron-util/blob/replace-remote/source/api.js |
2 | 2 | ||
3 | import electron from 'electron'; | 3 | import * as electron from 'electron'; |
4 | import { initialize } from '@electron/remote/main'; | 4 | import { initialize } from '@electron/remote/main'; |
5 | 5 | ||
6 | export const initializeRemote = () => { | 6 | export const initializeRemote = () => { |
7 | if (process.type !== 'browser') { | 7 | if (process.type !== 'browser') { |
8 | throw new Error('The remote api must be initialized from the main process.'); | 8 | throw new Error( |
9 | 'The remote api must be initialized from the main process.', | ||
10 | ); | ||
9 | } | 11 | } |
10 | 12 | ||
11 | initialize(); | 13 | initialize(); |
12 | }; | 14 | }; |
13 | 15 | ||
14 | export const remote = new Proxy({}, { | 16 | export const remote = new Proxy( |
15 | get: (_target, property) => { | 17 | {}, |
16 | // eslint-disable-next-line global-require | 18 | { |
17 | const remote = require('@electron/remote'); | 19 | get: (_target, property) => { |
18 | return remote[property]; | 20 | // eslint-disable-next-line global-require |
21 | const remote = require('@electron/remote'); | ||
22 | return remote[property]; | ||
23 | }, | ||
19 | }, | 24 | }, |
20 | }); | 25 | ); |
21 | 26 | ||
22 | export const api = new Proxy(electron, { | 27 | export const api = new Proxy(electron, { |
23 | get: (target, property) => { | 28 | get: (target, property) => { |
diff --git a/src/electron/exception.ts b/src/electron/exception.ts index 0065e2604..ada98d17b 100644 --- a/src/electron/exception.ts +++ b/src/electron/exception.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | process.on('uncaughtException', (err) => { | 1 | process.on('uncaughtException', err => { |
2 | // handle the error safely | 2 | // handle the error safely |
3 | console.error(err); | 3 | console.error(err); |
4 | }); | 4 | }); |
diff --git a/src/electron/ipc-api/appIndicator.ts b/src/electron/ipc-api/appIndicator.ts index a51ed8161..bd5f6a68f 100644 --- a/src/electron/ipc-api/appIndicator.ts +++ b/src/electron/ipc-api/appIndicator.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { app, ipcMain } from 'electron'; | 1 | import { app, ipcMain, BrowserWindow, Tray } from 'electron'; |
2 | import { join } from 'path'; | 2 | import { join } from 'path'; |
3 | import { autorun } from 'mobx'; | 3 | import { autorun } from 'mobx'; |
4 | import { isMac, isWindows, isLinux } from '../../environment'; | 4 | import { isMac, isWindows, isLinux } from '../../environment'; |
@@ -21,29 +21,38 @@ function getAsset(type: 'tray' | 'taskbar', asset: string) { | |||
21 | ); | 21 | ); |
22 | } | 22 | } |
23 | 23 | ||
24 | export default params => { | 24 | export default (params: { |
25 | mainWindow: BrowserWindow; | ||
26 | settings: any; | ||
27 | trayIcon: Tray; | ||
28 | }) => { | ||
25 | autorun(() => { | 29 | autorun(() => { |
26 | isTrayIconEnabled = params.settings.app.get('enableSystemTray'); | 30 | isTrayIconEnabled = params.settings.app.get('enableSystemTray'); |
27 | 31 | ||
28 | if (!isTrayIconEnabled) { | 32 | if (!isTrayIconEnabled) { |
33 | // @ts-expect-error Property 'hide' does not exist on type 'Tray'. | ||
29 | params.trayIcon.hide(); | 34 | params.trayIcon.hide(); |
30 | } else if (isTrayIconEnabled) { | 35 | } else if (isTrayIconEnabled) { |
36 | // @ts-expect-error Property 'show' does not exist on type 'Tray'. | ||
31 | params.trayIcon.show(); | 37 | params.trayIcon.show(); |
32 | } | 38 | } |
33 | }); | 39 | }); |
34 | 40 | ||
35 | ipcMain.on('updateAppIndicator', (_event, args) => { | 41 | ipcMain.on('updateAppIndicator', (_event, args) => { |
36 | // Flash TaskBar for windows, bounce Dock on Mac | 42 | // Flash TaskBar for windows, bounce Dock on Mac |
37 | if (!(app as any).mainWindow.isFocused() && params.settings.app.get('notifyTaskBarOnMessage')) { | 43 | if ( |
38 | if (isWindows) { | 44 | !params.mainWindow.isFocused() && |
39 | (app as any).mainWindow.flashFrame(true); | 45 | params.settings.app.get('notifyTaskBarOnMessage') |
40 | (app as any).mainWindow.once('focus', () => | 46 | ) { |
41 | (app as any).mainWindow.flashFrame(false), | 47 | if (isWindows) { |
42 | ); | 48 | params.mainWindow.flashFrame(true); |
43 | } else if (isMac) { | 49 | params.mainWindow.once('focus', () => |
44 | app.dock.bounce('informational'); | 50 | params.mainWindow.flashFrame(false), |
45 | } | 51 | ); |
52 | } else if (isMac) { | ||
53 | app.dock.bounce('informational'); | ||
46 | } | 54 | } |
55 | } | ||
47 | 56 | ||
48 | // Update badge | 57 | // Update badge |
49 | if (isMac && typeof args.indicator === 'string') { | 58 | if (isMac && typeof args.indicator === 'string') { |
@@ -57,6 +66,7 @@ export default params => { | |||
57 | if (isWindows) { | 66 | if (isWindows) { |
58 | if (typeof args.indicator === 'number' && args.indicator !== 0) { | 67 | if (typeof args.indicator === 'number' && args.indicator !== 0) { |
59 | params.mainWindow.setOverlayIcon( | 68 | params.mainWindow.setOverlayIcon( |
69 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'NativeImage | null'. | ||
60 | getAsset( | 70 | getAsset( |
61 | 'taskbar', | 71 | 'taskbar', |
62 | `${INDICATOR_TASKBAR}-${ | 72 | `${INDICATOR_TASKBAR}-${ |
@@ -67,6 +77,7 @@ export default params => { | |||
67 | ); | 77 | ); |
68 | } else if (typeof args.indicator === 'string') { | 78 | } else if (typeof args.indicator === 'string') { |
69 | params.mainWindow.setOverlayIcon( | 79 | params.mainWindow.setOverlayIcon( |
80 | // @ts-expect-error Argument of type 'string' is not assignable to parameter of type 'NativeImage | null'. | ||
70 | getAsset('taskbar', `${INDICATOR_TASKBAR}-alert`), | 81 | getAsset('taskbar', `${INDICATOR_TASKBAR}-alert`), |
71 | '', | 82 | '', |
72 | ); | 83 | ); |
@@ -76,6 +87,7 @@ export default params => { | |||
76 | } | 87 | } |
77 | 88 | ||
78 | // Update Tray | 89 | // Update Tray |
90 | // @ts-expect-error Property 'setIndicator' does not exist on type 'Tray'. | ||
79 | params.trayIcon.setIndicator(args.indicator); | 91 | params.trayIcon.setIndicator(args.indicator); |
80 | }); | 92 | }); |
81 | }; | 93 | }; |
diff --git a/src/electron/ipc-api/index.ts b/src/electron/ipc-api/index.ts index f03f61517..1f69c04ee 100644 --- a/src/electron/ipc-api/index.ts +++ b/src/electron/ipc-api/index.ts | |||
@@ -12,7 +12,7 @@ import focusState from './focusState'; | |||
12 | export default (params: { | 12 | export default (params: { |
13 | mainWindow: BrowserWindow; | 13 | mainWindow: BrowserWindow; |
14 | settings: any; | 14 | settings: any; |
15 | tray: Tray; | 15 | trayIcon: Tray; |
16 | }) => { | 16 | }) => { |
17 | settings(params); | 17 | settings(params); |
18 | sessionStorage(); | 18 | sessionStorage(); |
diff --git a/src/electron/ipc-api/localServer.ts b/src/electron/ipc-api/localServer.ts index 7ee642101..04ddc976a 100644 --- a/src/electron/ipc-api/localServer.ts +++ b/src/electron/ipc-api/localServer.ts | |||
@@ -1,12 +1,12 @@ | |||
1 | import { ipcMain, BrowserWindow } from 'electron'; | 1 | import { ipcMain, BrowserWindow } from 'electron'; |
2 | import net from 'net'; | 2 | import { createServer } from 'net'; |
3 | import { LOCAL_HOSTNAME, LOCAL_PORT } from '../../config'; | 3 | import { LOCAL_HOSTNAME, LOCAL_PORT } from '../../config'; |
4 | import { userDataPath } from '../../environment-remote'; | 4 | import { userDataPath } from '../../environment-remote'; |
5 | import { server } from '../../internal-server/start'; | 5 | import { server } from '../../internal-server/start'; |
6 | 6 | ||
7 | const portInUse = (port: number): Promise<boolean> => | 7 | const portInUse = (port: number): Promise<boolean> => |
8 | new Promise(resolve => { | 8 | new Promise(resolve => { |
9 | const server = net.createServer(socket => { | 9 | const server = createServer(socket => { |
10 | socket.write('Echo server\r\n'); | 10 | socket.write('Echo server\r\n'); |
11 | socket.pipe(socket); | 11 | socket.pipe(socket); |
12 | }); | 12 | }); |
diff --git a/src/electron/ipc-api/sessionStorage.ts b/src/electron/ipc-api/sessionStorage.ts index 3eda568a1..1ff0a51ea 100644 --- a/src/electron/ipc-api/sessionStorage.ts +++ b/src/electron/ipc-api/sessionStorage.ts | |||
@@ -6,7 +6,11 @@ const debug = require('debug')('Ferdi:ipcApi:sessionStorage'); | |||
6 | 6 | ||
7 | function deduceSession(serviceId: string | undefined | null): Session { | 7 | function deduceSession(serviceId: string | undefined | null): Session { |
8 | if (serviceId) { | 8 | if (serviceId) { |
9 | return session.fromPartition(serviceId === TODOS_PARTITION_ID ? TODOS_PARTITION_ID : `persist:service-${serviceId}`); | 9 | return session.fromPartition( |
10 | serviceId === TODOS_PARTITION_ID | ||
11 | ? TODOS_PARTITION_ID | ||
12 | : `persist:service-${serviceId}`, | ||
13 | ); | ||
10 | } | 14 | } |
11 | return session.defaultSession; | 15 | return session.defaultSession; |
12 | } | 16 | } |
diff --git a/src/enforce-macos-app-location.ts b/src/enforce-macos-app-location.ts index 0f858013d..0e6bf9ecc 100644 --- a/src/enforce-macos-app-location.ts +++ b/src/enforce-macos-app-location.ts | |||
@@ -12,11 +12,9 @@ export function enforceMacOSAppLocation() { | |||
12 | const clickedButtonIndex = api.dialog.showMessageBoxSync({ | 12 | const clickedButtonIndex = api.dialog.showMessageBoxSync({ |
13 | type: 'error', | 13 | type: 'error', |
14 | message: 'Move to Applications folder?', | 14 | message: 'Move to Applications folder?', |
15 | detail: 'Ferdi must live in the Applications folder to be able to run correctly.', | 15 | detail: |
16 | buttons: [ | 16 | 'Ferdi must live in the Applications folder to be able to run correctly.', |
17 | 'Move to Applications folder', | 17 | buttons: ['Move to Applications folder', 'Quit Ferdi'], |
18 | 'Quit Ferdi', | ||
19 | ], | ||
20 | defaultId: 0, | 18 | defaultId: 0, |
21 | cancelId: 1, | 19 | cancelId: 1, |
22 | }); | 20 | }); |
@@ -28,13 +26,13 @@ export function enforceMacOSAppLocation() { | |||
28 | 26 | ||
29 | api.app.moveToApplicationsFolder({ | 27 | api.app.moveToApplicationsFolder({ |
30 | conflictHandler: conflict => { | 28 | conflictHandler: conflict => { |
31 | if (conflict === 'existsAndRunning') { // Can't replace the active version of the app | 29 | if (conflict === 'existsAndRunning') { |
30 | // Can't replace the active version of the app | ||
32 | api.dialog.showMessageBoxSync({ | 31 | api.dialog.showMessageBoxSync({ |
33 | type: 'error', | 32 | type: 'error', |
34 | message: 'Another version of Ferdi is currently running. Quit it, then launch this version of the app again.', | 33 | message: |
35 | buttons: [ | 34 | 'Another version of Ferdi is currently running. Quit it, then launch this version of the app again.', |
36 | 'OK', | 35 | buttons: ['OK'], |
37 | ], | ||
38 | }); | 36 | }); |
39 | 37 | ||
40 | api.app.quit(); | 38 | api.app.quit(); |
diff --git a/src/environment-remote.ts b/src/environment-remote.ts index 192510f37..c87e89772 100644 --- a/src/environment-remote.ts +++ b/src/environment-remote.ts | |||
@@ -14,7 +14,13 @@ import { | |||
14 | LOCAL_TODOS_FRONTEND_URL, | 14 | LOCAL_TODOS_FRONTEND_URL, |
15 | PRODUCTION_TODOS_FRONTEND_URL, | 15 | PRODUCTION_TODOS_FRONTEND_URL, |
16 | } from './config'; | 16 | } from './config'; |
17 | import { chromeVersion, electronVersion, isWindows, nodeVersion, osArch } from './environment'; | 17 | import { |
18 | chromeVersion, | ||
19 | electronVersion, | ||
20 | isWindows, | ||
21 | nodeVersion, | ||
22 | osArch, | ||
23 | } from './environment'; | ||
18 | 24 | ||
19 | // @ts-expect-error Cannot find module './buildInfo.json' or its corresponding type declarations. | 25 | // @ts-expect-error Cannot find module './buildInfo.json' or its corresponding type declarations. |
20 | import * as buildInfo from './buildInfo.json'; | 26 | import * as buildInfo from './buildInfo.json'; |
@@ -28,14 +34,20 @@ if (process.env.FERDI_APPDATA_DIR != null) { | |||
28 | app.setPath('appData', process.env.FERDI_APPDATA_DIR); | 34 | app.setPath('appData', process.env.FERDI_APPDATA_DIR); |
29 | app.setPath('userData', app.getPath('appData')); | 35 | app.setPath('userData', app.getPath('appData')); |
30 | } else if (process.env.PORTABLE_EXECUTABLE_DIR != null) { | 36 | } else if (process.env.PORTABLE_EXECUTABLE_DIR != null) { |
31 | app.setPath('appData', join(process.env.PORTABLE_EXECUTABLE_DIR, `${app.name}AppData`)); | 37 | app.setPath( |
38 | 'appData', | ||
39 | join(process.env.PORTABLE_EXECUTABLE_DIR, `${app.name}AppData`), | ||
40 | ); | ||
32 | app.setPath('userData', join(app.getPath('appData'), `${app.name}AppData`)); | 41 | app.setPath('userData', join(app.getPath('appData'), `${app.name}AppData`)); |
33 | } else if (isWindows && process.env.APPDATA != null) { | 42 | } else if (isWindows && process.env.APPDATA != null) { |
34 | app.setPath('appData', process.env.APPDATA); | 43 | app.setPath('appData', process.env.APPDATA); |
35 | app.setPath('userData', join(app.getPath('appData'), app.name)); | 44 | app.setPath('userData', join(app.getPath('appData'), app.name)); |
36 | } | 45 | } |
37 | 46 | ||
38 | export const isDevMode = process.env.ELECTRON_IS_DEV !== undefined ? Number.parseInt(process.env.ELECTRON_IS_DEV, 10) === 1 : !app.isPackaged; | 47 | export const isDevMode = |
48 | process.env.ELECTRON_IS_DEV !== undefined | ||
49 | ? Number.parseInt(process.env.ELECTRON_IS_DEV, 10) === 1 | ||
50 | : !app.isPackaged; | ||
39 | if (isDevMode) { | 51 | if (isDevMode) { |
40 | app.setPath('userData', join(app.getPath('appData'), `${app.name}Dev`)); | 52 | app.setPath('userData', join(app.getPath('appData'), `${app.name}Dev`)); |
41 | } | 53 | } |
diff --git a/src/environment.ts b/src/environment.ts index ce2a77e7a..9b727a607 100644 --- a/src/environment.ts +++ b/src/environment.ts | |||
@@ -1,6 +1,6 @@ | |||
1 | // Note: This file has now become devoid of all references to values deduced from the remote process - all those now live in the `environment-remote.js` file | 1 | // Note: This file has now become devoid of all references to values deduced from the remote process - all those now live in the `environment-remote.js` file |
2 | 2 | ||
3 | import os from 'os'; | 3 | import { arch, release } from 'os'; |
4 | 4 | ||
5 | export const isMac = process.platform === 'darwin'; | 5 | export const isMac = process.platform === 'darwin'; |
6 | export const isWindows = process.platform === 'win32'; | 6 | export const isWindows = process.platform === 'win32'; |
@@ -10,9 +10,8 @@ export const electronVersion = process.versions.electron; | |||
10 | export const chromeVersion = process.versions.chrome; | 10 | export const chromeVersion = process.versions.chrome; |
11 | export const nodeVersion = process.versions.node; | 11 | export const nodeVersion = process.versions.node; |
12 | 12 | ||
13 | export const osPlatform = os.platform(); | 13 | export const osArch = arch(); |
14 | export const osArch = os.arch(); | 14 | export const osRelease = release(); |
15 | export const osRelease = os.release(); | ||
16 | export const is64Bit = osArch.match(/64/); | 15 | export const is64Bit = osArch.match(/64/); |
17 | 16 | ||
18 | // for accelerator, show the shortform that electron/OS understands | 17 | // for accelerator, show the shortform that electron/OS understands |
diff --git a/src/features/basicAuth/Component.js b/src/features/basicAuth/Component.js index 3cf937f98..652233e55 100644 --- a/src/features/basicAuth/Component.js +++ b/src/features/basicAuth/Component.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import injectSheet from 'react-jss'; | 3 | import injectSheet from 'react-jss'; |
4 | import { observer } from 'mobx-react'; | 4 | import { observer } from 'mobx-react'; |
diff --git a/src/features/basicAuth/index.js b/src/features/basicAuth/index.ts index e43d51d15..149ab6c19 100644 --- a/src/features/basicAuth/index.js +++ b/src/features/basicAuth/index.ts | |||
@@ -1,4 +1,4 @@ | |||
1 | import { ipcRenderer } from 'electron'; | 1 | import { AuthInfo, BrowserWindow, ipcRenderer } from 'electron'; |
2 | 2 | ||
3 | import BasicAuthComponent from './Component'; | 3 | import BasicAuthComponent from './Component'; |
4 | 4 | ||
@@ -11,7 +11,7 @@ const state = ModalState; | |||
11 | export default function initialize() { | 11 | export default function initialize() { |
12 | debug('Initialize basicAuth feature'); | 12 | debug('Initialize basicAuth feature'); |
13 | 13 | ||
14 | window.ferdi.features.basicAuth = { | 14 | window['ferdi'].features.basicAuth = { |
15 | state, | 15 | state, |
16 | }; | 16 | }; |
17 | 17 | ||
@@ -23,7 +23,7 @@ export default function initialize() { | |||
23 | }); | 23 | }); |
24 | } | 24 | } |
25 | 25 | ||
26 | export function mainIpcHandler(mainWindow, authInfo) { | 26 | export function mainIpcHandler(mainWindow: BrowserWindow, authInfo: AuthInfo) { |
27 | debug('Sending basic auth call', authInfo); | 27 | debug('Sending basic auth call', authInfo); |
28 | 28 | ||
29 | mainWindow.webContents.send('feature:basic-auth-request', { | 29 | mainWindow.webContents.send('feature:basic-auth-request', { |
diff --git a/src/features/nightlyBuilds/Component.js b/src/features/nightlyBuilds/Component.js index 814d529d9..64f782c8f 100644 --- a/src/features/nightlyBuilds/Component.js +++ b/src/features/nightlyBuilds/Component.js | |||
@@ -1,9 +1,9 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, inject } from 'mobx-react'; | 3 | import { observer, inject } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
6 | import { H1 } from '@meetfranz/ui'; | 6 | import { H1 } from '../../components/ui/headline'; |
7 | 7 | ||
8 | import Modal from '../../components/ui/Modal'; | 8 | import Modal from '../../components/ui/Modal'; |
9 | import Button from '../../components/ui/Button'; | 9 | import Button from '../../components/ui/Button'; |
diff --git a/src/features/nightlyBuilds/index.js b/src/features/nightlyBuilds/index.ts index 89bcb5cb3..14afbaf1b 100644 --- a/src/features/nightlyBuilds/index.js +++ b/src/features/nightlyBuilds/index.ts | |||
@@ -14,26 +14,26 @@ export default function initialize() { | |||
14 | } | 14 | } |
15 | 15 | ||
16 | function toggleFeature() { | 16 | function toggleFeature() { |
17 | if (window.ferdi.stores.settings.app.nightly) { | 17 | if (window['ferdi'].stores.settings.app.nightly) { |
18 | window.ferdi.actions.settings.update({ | 18 | window['ferdi'].actions.settings.update({ |
19 | type: 'app', | 19 | type: 'app', |
20 | data: { | 20 | data: { |
21 | nightly: false, | 21 | nightly: false, |
22 | }, | 22 | }, |
23 | }); | 23 | }); |
24 | window.ferdi.actions.user.update({ | 24 | window['ferdi'].actions.user.update({ |
25 | userData: { | 25 | userData: { |
26 | nightly: false, | 26 | nightly: false, |
27 | }, | 27 | }, |
28 | }); | 28 | }); |
29 | } else { | 29 | } else { |
30 | // We need to close the settings, otherwise the modal will be drawn under the settings window | 30 | // We need to close the settings, otherwise the modal will be drawn under the settings window |
31 | window.ferdi.actions.ui.closeSettings(); | 31 | window['ferdi'].actions.ui.closeSettings(); |
32 | showModal(); | 32 | showModal(); |
33 | } | 33 | } |
34 | } | 34 | } |
35 | 35 | ||
36 | window.ferdi.features.nightlyBuilds = { | 36 | window['ferdi'].features.nightlyBuilds = { |
37 | state, | 37 | state, |
38 | showModal, | 38 | showModal, |
39 | toggleFeature, | 39 | toggleFeature, |
diff --git a/src/features/publishDebugInfo/Component.js b/src/features/publishDebugInfo/Component.js index 5b5036752..30bdc13b6 100644 --- a/src/features/publishDebugInfo/Component.js +++ b/src/features/publishDebugInfo/Component.js | |||
@@ -1,10 +1,11 @@ | |||
1 | import { H1 } from '@meetfranz/ui'; | ||
2 | import { inject, observer } from 'mobx-react'; | 1 | import { inject, observer } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
4 | import React, { Component } from 'react'; | 3 | import { Component } from 'react'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
6 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
7 | import { state as ModalState } from './store'; | 6 | import { state as ModalState } from './store'; |
7 | |||
8 | import { H1 } from '../../components/ui/headline'; | ||
8 | import { sendAuthRequest } from '../../api/utils/auth'; | 9 | import { sendAuthRequest } from '../../api/utils/auth'; |
9 | import Button from '../../components/ui/Button'; | 10 | import Button from '../../components/ui/Button'; |
10 | import Input from '../../components/ui/Input'; | 11 | import Input from '../../components/ui/Input'; |
diff --git a/src/features/quickSwitch/Component.js b/src/features/quickSwitch/Component.js index f21db0ebd..d5cb9179f 100644 --- a/src/features/quickSwitch/Component.js +++ b/src/features/quickSwitch/Component.js | |||
@@ -1,14 +1,14 @@ | |||
1 | import React, { Component, createRef } from 'react'; | 1 | import { Component, createRef } from 'react'; |
2 | import { getCurrentWindow } from '@electron/remote'; | 2 | import { getCurrentWindow } from '@electron/remote'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer, inject } from 'mobx-react'; | 4 | import { observer, inject } from 'mobx-react'; |
5 | import { reaction } from 'mobx'; | 5 | import { reaction } from 'mobx'; |
6 | import injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
7 | import { defineMessages, injectIntl } from 'react-intl'; | 7 | import { defineMessages, injectIntl } from 'react-intl'; |
8 | import { Input } from '@meetfranz/forms'; | ||
9 | import { H1 } from '@meetfranz/ui'; | ||
10 | |||
11 | import { compact, invoke } from 'lodash'; | 8 | import { compact, invoke } from 'lodash'; |
9 | |||
10 | import { Input } from '../../components/ui/input/index'; | ||
11 | import { H1 } from '../../components/ui/headline'; | ||
12 | import Modal from '../../components/ui/Modal'; | 12 | import Modal from '../../components/ui/Modal'; |
13 | import { state as ModalState } from './store'; | 13 | import { state as ModalState } from './store'; |
14 | import ServicesStore from '../../stores/ServicesStore'; | 14 | import ServicesStore from '../../stores/ServicesStore'; |
diff --git a/src/features/quickSwitch/index.js b/src/features/quickSwitch/index.ts index a16017219..e9cc36b2a 100644 --- a/src/features/quickSwitch/index.js +++ b/src/features/quickSwitch/index.ts | |||
@@ -12,7 +12,7 @@ export default function initialize() { | |||
12 | state.isModalVisible = true; | 12 | state.isModalVisible = true; |
13 | } | 13 | } |
14 | 14 | ||
15 | window.ferdi.features.quickSwitch = { | 15 | window['ferdi'].features.quickSwitch = { |
16 | state, | 16 | state, |
17 | showModal, | 17 | showModal, |
18 | }; | 18 | }; |
diff --git a/src/features/settingsWS/actions.ts b/src/features/settingsWS/actions.ts index 631670c8a..03a398eb5 100755 --- a/src/features/settingsWS/actions.ts +++ b/src/features/settingsWS/actions.ts | |||
@@ -1,10 +1,13 @@ | |||
1 | import PropTypes from 'prop-types'; | 1 | import PropTypes from 'prop-types'; |
2 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; | 2 | import { createActionsFromDefinitions } from '../../actions/lib/actions'; |
3 | 3 | ||
4 | export const settingsWSActions = createActionsFromDefinitions({ | 4 | export const settingsWSActions = createActionsFromDefinitions( |
5 | greet: { | 5 | { |
6 | name: PropTypes.string.isRequired, | 6 | greet: { |
7 | name: PropTypes.string.isRequired, | ||
8 | }, | ||
7 | }, | 9 | }, |
8 | }, PropTypes.checkPropTypes); | 10 | PropTypes.checkPropTypes, |
11 | ); | ||
9 | 12 | ||
10 | export default settingsWSActions; | 13 | export default settingsWSActions; |
diff --git a/src/features/todos/components/TodosWebview.js b/src/features/todos/components/TodosWebview.js index 2dc30cdf2..1d423544b 100644 --- a/src/features/todos/components/TodosWebview.js +++ b/src/features/todos/components/TodosWebview.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
@@ -7,14 +7,15 @@ import classnames from 'classnames'; | |||
7 | 7 | ||
8 | import { TODOS_PARTITION_ID } from '../../../config'; | 8 | import { TODOS_PARTITION_ID } from '../../../config'; |
9 | 9 | ||
10 | const styles = (theme) => ({ | 10 | const styles = theme => ({ |
11 | root: { | 11 | root: { |
12 | background: theme.colorBackground, | 12 | background: theme.colorBackground, |
13 | position: 'relative', | 13 | position: 'relative', |
14 | borderLeft: [1, 'solid', theme.todos.todosLayer.borderLeftColor], | 14 | borderLeft: [1, 'solid', theme.todos.todosLayer.borderLeftColor], |
15 | zIndex: 300, | 15 | zIndex: 300, |
16 | 16 | ||
17 | transform: ({ isVisible, width, isTodosServiceActive }) => `translateX(${isVisible || isTodosServiceActive ? 0 : width}px)`, | 17 | transform: ({ isVisible, width, isTodosServiceActive }) => |
18 | `translateX(${isVisible || isTodosServiceActive ? 0 : width}px)`, | ||
18 | 19 | ||
19 | '& webview': { | 20 | '& webview': { |
20 | height: '100%', | 21 | height: '100%', |
@@ -79,7 +80,7 @@ class TodosWebview extends Component { | |||
79 | this.node.addEventListener('mouseleave', this.stopResize.bind(this)); | 80 | this.node.addEventListener('mouseleave', this.stopResize.bind(this)); |
80 | } | 81 | } |
81 | 82 | ||
82 | startResize = (event) => { | 83 | startResize = event => { |
83 | this.setState({ | 84 | this.setState({ |
84 | isDragging: true, | 85 | isDragging: true, |
85 | initialPos: event.clientX, | 86 | initialPos: event.clientX, |
@@ -126,7 +127,7 @@ class TodosWebview extends Component { | |||
126 | startListeningToIpcMessages() { | 127 | startListeningToIpcMessages() { |
127 | const { handleClientMessage } = this.props; | 128 | const { handleClientMessage } = this.props; |
128 | if (!this.webview) return; | 129 | if (!this.webview) return; |
129 | this.webview.addEventListener('ipc-message', (e) => { | 130 | this.webview.addEventListener('ipc-message', e => { |
130 | // console.log(e); | 131 | // console.log(e); |
131 | handleClientMessage({ channel: e.channel, message: e.args[0] }); | 132 | handleClientMessage({ channel: e.channel, message: e.args[0] }); |
132 | }); | 133 | }); |
@@ -159,7 +160,7 @@ class TodosWebview extends Component { | |||
159 | })} | 160 | })} |
160 | style={{ width: displayedWidth }} | 161 | style={{ width: displayedWidth }} |
161 | onMouseUp={() => this.stopResize()} | 162 | onMouseUp={() => this.stopResize()} |
162 | ref={(node) => { | 163 | ref={node => { |
163 | this.node = node; | 164 | this.node = node; |
164 | }} | 165 | }} |
165 | id="todos-panel" | 166 | id="todos-panel" |
@@ -170,7 +171,7 @@ class TodosWebview extends Component { | |||
170 | left: delta, | 171 | left: delta, |
171 | ...(isDragging ? { width: 600, marginLeft: -200 } : {}), | 172 | ...(isDragging ? { width: 600, marginLeft: -200 } : {}), |
172 | }} // This hack is required as resizing with webviews beneath behaves quite bad | 173 | }} // This hack is required as resizing with webviews beneath behaves quite bad |
173 | onMouseDown={(e) => this.startResize(e)} | 174 | onMouseDown={e => this.startResize(e)} |
174 | /> | 175 | /> |
175 | {isDragging && ( | 176 | {isDragging && ( |
176 | <div | 177 | <div |
@@ -188,7 +189,7 @@ class TodosWebview extends Component { | |||
188 | }} | 189 | }} |
189 | partition={TODOS_PARTITION_ID} | 190 | partition={TODOS_PARTITION_ID} |
190 | preload="./features/todos/preload.js" | 191 | preload="./features/todos/preload.js" |
191 | ref={(webview) => { | 192 | ref={webview => { |
192 | this.webview = webview ? webview.view : null; | 193 | this.webview = webview ? webview.view : null; |
193 | }} | 194 | }} |
194 | useragent={userAgent} | 195 | useragent={userAgent} |
diff --git a/src/features/todos/containers/TodosScreen.js b/src/features/todos/containers/TodosScreen.js index d05e24e56..536810d2d 100644 --- a/src/features/todos/containers/TodosScreen.js +++ b/src/features/todos/containers/TodosScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { observer, inject } from 'mobx-react'; | 2 | import { observer, inject } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | 4 | ||
@@ -10,24 +10,31 @@ import { TODOS_MIN_WIDTH } from '../../../config'; | |||
10 | import { todoActions } from '../actions'; | 10 | import { todoActions } from '../actions'; |
11 | import ServicesStore from '../../../stores/ServicesStore'; | 11 | import ServicesStore from '../../../stores/ServicesStore'; |
12 | 12 | ||
13 | @inject('stores', 'actions') @observer | 13 | @inject('stores', 'actions') |
14 | @observer | ||
14 | class TodosScreen extends Component { | 15 | class TodosScreen extends Component { |
15 | render() { | 16 | render() { |
16 | if (!todosStore || !todosStore.isFeatureActive || todosStore.isTodosPanelForceHidden) { | 17 | if ( |
18 | !todosStore || | ||
19 | !todosStore.isFeatureActive || | ||
20 | todosStore.isTodosPanelForceHidden | ||
21 | ) { | ||
17 | return null; | 22 | return null; |
18 | } | 23 | } |
19 | 24 | ||
20 | return ( | 25 | return ( |
21 | <ErrorBoundary> | 26 | <ErrorBoundary> |
22 | <TodosWebview | 27 | <TodosWebview |
23 | isTodosServiceActive={this.props.stores.services.isTodosServiceActive || false} | 28 | isTodosServiceActive={ |
29 | this.props.stores.services.isTodosServiceActive || false | ||
30 | } | ||
24 | isVisible={todosStore.isTodosPanelVisible} | 31 | isVisible={todosStore.isTodosPanelVisible} |
25 | togglePanel={todoActions.toggleTodosPanel} | 32 | togglePanel={todoActions.toggleTodosPanel} |
26 | handleClientMessage={todoActions.handleClientMessage} | 33 | handleClientMessage={todoActions.handleClientMessage} |
27 | setTodosWebview={(webview) => todoActions.setTodosWebview({ webview })} | 34 | setTodosWebview={webview => todoActions.setTodosWebview({ webview })} |
28 | width={todosStore.width} | 35 | width={todosStore.width} |
29 | minWidth={TODOS_MIN_WIDTH} | 36 | minWidth={TODOS_MIN_WIDTH} |
30 | resize={(width) => todoActions.resize({ width })} | 37 | resize={width => todoActions.resize({ width })} |
31 | userAgent={todosStore.userAgent} | 38 | userAgent={todosStore.userAgent} |
32 | todoUrl={todosStore.todoUrl} | 39 | todoUrl={todosStore.todoUrl} |
33 | isTodoUrlValid={todosStore.isTodoUrlValid} | 40 | isTodoUrlValid={todosStore.isTodoUrlValid} |
diff --git a/src/features/todos/preload.js b/src/features/todos/preload.ts index 3b86ddbc5..31d473051 100644 --- a/src/features/todos/preload.js +++ b/src/features/todos/preload.ts | |||
@@ -14,7 +14,7 @@ let hostMessageListener = ({ action }) => { | |||
14 | } | 14 | } |
15 | }; | 15 | }; |
16 | 16 | ||
17 | window.ferdi = { | 17 | window['ferdi'] = { |
18 | onInitialize(ipcHostMessageListener) { | 18 | onInitialize(ipcHostMessageListener) { |
19 | hostMessageListener = ipcHostMessageListener; | 19 | hostMessageListener = ipcHostMessageListener; |
20 | ipcRenderer.sendToHost(IPC.TODOS_CLIENT_CHANNEL, { | 20 | ipcRenderer.sendToHost(IPC.TODOS_CLIENT_CHANNEL, { |
diff --git a/src/features/todos/store.js b/src/features/todos/store.js index ec06c279d..010a029ff 100644 --- a/src/features/todos/store.js +++ b/src/features/todos/store.js | |||
@@ -1,7 +1,7 @@ | |||
1 | import { ThemeType } from '@meetfranz/theme'; | ||
2 | import { computed, action, observable } from 'mobx'; | 1 | import { computed, action, observable } from 'mobx'; |
3 | import localStorage from 'mobx-localstorage'; | 2 | import localStorage from 'mobx-localstorage'; |
4 | 3 | ||
4 | import { ThemeType } from '../../themes'; | ||
5 | import { todoActions } from './actions'; | 5 | import { todoActions } from './actions'; |
6 | import { | 6 | import { |
7 | CUSTOM_TODO_SERVICE, | 7 | CUSTOM_TODO_SERVICE, |
diff --git a/src/features/utils/ActionBinding.ts b/src/features/utils/ActionBinding.ts index 787166d44..16308fae4 100644 --- a/src/features/utils/ActionBinding.ts +++ b/src/features/utils/ActionBinding.ts | |||
@@ -24,6 +24,5 @@ export default class ActionBinding { | |||
24 | } | 24 | } |
25 | } | 25 | } |
26 | 26 | ||
27 | export const createActionBindings = (actions) => ( | 27 | export const createActionBindings = actions => |
28 | actions.map((a) => new ActionBinding(a)) | 28 | actions.map(a => new ActionBinding(a)); |
29 | ); | ||
diff --git a/src/features/utils/FeatureStore.test.js b/src/features/utils/FeatureStore.test.js index 92308bf52..1995431bd 100644 --- a/src/features/utils/FeatureStore.test.js +++ b/src/features/utils/FeatureStore.test.js | |||
@@ -5,9 +5,12 @@ import { createActionsFromDefinitions } from '../../actions/lib/actions'; | |||
5 | import { createActionBindings } from './ActionBinding'; | 5 | import { createActionBindings } from './ActionBinding'; |
6 | import { createReactions } from '../../stores/lib/Reaction'; | 6 | import { createReactions } from '../../stores/lib/Reaction'; |
7 | 7 | ||
8 | const actions = createActionsFromDefinitions({ | 8 | const actions = createActionsFromDefinitions( |
9 | countUp: {}, | 9 | { |
10 | }, PropTypes.checkPropTypes); | 10 | countUp: {}, |
11 | }, | ||
12 | PropTypes.checkPropTypes, | ||
13 | ); | ||
11 | 14 | ||
12 | class TestFeatureStore extends FeatureStore { | 15 | class TestFeatureStore extends FeatureStore { |
13 | @observable count = 0; | 16 | @observable count = 0; |
@@ -15,12 +18,10 @@ class TestFeatureStore extends FeatureStore { | |||
15 | reactionInvokedCount = 0; | 18 | reactionInvokedCount = 0; |
16 | 19 | ||
17 | start() { | 20 | start() { |
18 | this._registerActions(createActionBindings([ | 21 | this._registerActions( |
19 | [actions.countUp, this._countUp], | 22 | createActionBindings([[actions.countUp, this._countUp]]), |
20 | ])); | 23 | ); |
21 | this._registerReactions(createReactions([ | 24 | this._registerReactions(createReactions([this._countReaction])); |
22 | this._countReaction, | ||
23 | ])); | ||
24 | } | 25 | } |
25 | 26 | ||
26 | _countUp = () => { | 27 | _countUp = () => { |
@@ -29,7 +30,7 @@ class TestFeatureStore extends FeatureStore { | |||
29 | 30 | ||
30 | _countReaction = () => { | 31 | _countReaction = () => { |
31 | this.reactionInvokedCount += 1; | 32 | this.reactionInvokedCount += 1; |
32 | } | 33 | }; |
33 | } | 34 | } |
34 | 35 | ||
35 | describe('FeatureStore', () => { | 36 | describe('FeatureStore', () => { |
diff --git a/src/features/webControls/components/WebControls.js b/src/features/webControls/components/WebControls.js index 97fa20dcc..5650d4cd1 100644 --- a/src/features/webControls/components/WebControls.js +++ b/src/features/webControls/components/WebControls.js | |||
@@ -1,8 +1,7 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { createRef, Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { Icon } from '@meetfranz/ui'; | ||
6 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
7 | 6 | ||
8 | import { | 7 | import { |
@@ -13,6 +12,8 @@ import { | |||
13 | mdiEarth, | 12 | mdiEarth, |
14 | } from '@mdi/js'; | 13 | } from '@mdi/js'; |
15 | 14 | ||
15 | import { Icon } from '../../../components/ui/icon'; | ||
16 | |||
16 | const messages = defineMessages({ | 17 | const messages = defineMessages({ |
17 | goHome: { | 18 | goHome: { |
18 | id: 'webControls.goHome', | 19 | id: 'webControls.goHome', |
@@ -121,7 +122,7 @@ class WebControls extends Component { | |||
121 | } | 122 | } |
122 | } | 123 | } |
123 | 124 | ||
124 | inputRef = React.createRef(); | 125 | inputRef = createRef(); |
125 | 126 | ||
126 | state = { | 127 | state = { |
127 | inputUrl: '', | 128 | inputUrl: '', |
diff --git a/src/features/webControls/containers/WebControlsScreen.js b/src/features/webControls/containers/WebControlsScreen.js index 0273bb13e..6fba5db86 100644 --- a/src/features/webControls/containers/WebControlsScreen.js +++ b/src/features/webControls/containers/WebControlsScreen.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { observer, inject } from 'mobx-react'; | 2 | import { observer, inject } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | 4 | ||
diff --git a/src/features/workspaces/components/CreateWorkspaceForm.js b/src/features/workspaces/components/CreateWorkspaceForm.js index c9b05b87f..75f6d9f4a 100644 --- a/src/features/workspaces/components/CreateWorkspaceForm.js +++ b/src/features/workspaces/components/CreateWorkspaceForm.js | |||
@@ -1,9 +1,11 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import { Input, Button } from '@meetfranz/forms'; | ||
6 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
6 | |||
7 | import { Input } from '../../../components/ui/input/index'; | ||
8 | import { Button } from '../../../components/ui/button/index'; | ||
7 | import Form from '../../../lib/Form'; | 9 | import Form from '../../../lib/Form'; |
8 | import { required } from '../../../helpers/validation-helpers'; | 10 | import { required } from '../../../helpers/validation-helpers'; |
9 | import { workspaceStore } from '../index'; | 11 | import { workspaceStore } from '../index'; |
diff --git a/src/features/workspaces/components/EditWorkspaceForm.js b/src/features/workspaces/components/EditWorkspaceForm.js index f562733dd..fa3ea4289 100644 --- a/src/features/workspaces/components/EditWorkspaceForm.js +++ b/src/features/workspaces/components/EditWorkspaceForm.js | |||
@@ -1,11 +1,12 @@ | |||
1 | import React, { Component, Fragment } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import { Link } from 'react-router'; | 5 | import { Link } from 'react-router'; |
6 | import { Input, Button } from '@meetfranz/forms'; | ||
7 | import injectSheet from 'react-jss'; | 6 | import injectSheet from 'react-jss'; |
8 | 7 | ||
8 | import { Input } from '../../../components/ui/input/index'; | ||
9 | import { Button } from '../../../components/ui/button/index'; | ||
9 | import Workspace from '../models/Workspace'; | 10 | import Workspace from '../models/Workspace'; |
10 | import Service from '../../../models/Service'; | 11 | import Service from '../../../models/Service'; |
11 | import Form from '../../../lib/Form'; | 12 | import Form from '../../../lib/Form'; |
diff --git a/src/features/workspaces/components/WorkspaceDrawer.js b/src/features/workspaces/components/WorkspaceDrawer.js index 3dac77bc2..590efacd0 100644 --- a/src/features/workspaces/components/WorkspaceDrawer.js +++ b/src/features/workspaces/components/WorkspaceDrawer.js | |||
@@ -1,12 +1,14 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import { defineMessages, injectIntl } from 'react-intl'; | 5 | import { defineMessages, injectIntl } from 'react-intl'; |
6 | import { H1, Icon } from '@meetfranz/ui'; | ||
7 | import ReactTooltip from 'react-tooltip'; | 6 | import ReactTooltip from 'react-tooltip'; |
8 | 7 | ||
9 | import { mdiPlusBox, mdiCog } from '@mdi/js'; | 8 | import { mdiPlusBox, mdiCog } from '@mdi/js'; |
9 | |||
10 | import { H1 } from '../../../components/ui/headline'; | ||
11 | import { Icon } from '../../../components/ui/icon'; | ||
10 | import WorkspaceDrawerItem from './WorkspaceDrawerItem'; | 12 | import WorkspaceDrawerItem from './WorkspaceDrawerItem'; |
11 | import { workspaceActions } from '../actions'; | 13 | import { workspaceActions } from '../actions'; |
12 | import { workspaceStore } from '../index'; | 14 | import { workspaceStore } from '../index'; |
diff --git a/src/features/workspaces/components/WorkspaceDrawerItem.js b/src/features/workspaces/components/WorkspaceDrawerItem.js index 4afb9c108..d3c9fa767 100644 --- a/src/features/workspaces/components/WorkspaceDrawerItem.js +++ b/src/features/workspaces/components/WorkspaceDrawerItem.js | |||
@@ -1,5 +1,5 @@ | |||
1 | import { Menu } from '@electron/remote'; | 1 | import { Menu } from '@electron/remote'; |
2 | import React, { Component } from 'react'; | 2 | import { Component } from 'react'; |
3 | import PropTypes from 'prop-types'; | 3 | import PropTypes from 'prop-types'; |
4 | import { observer } from 'mobx-react'; | 4 | import { observer } from 'mobx-react'; |
5 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
@@ -118,14 +118,12 @@ class WorkspaceDrawerItem extends Component { | |||
118 | isActive ? classes.isActiveItem : null, | 118 | isActive ? classes.isActiveItem : null, |
119 | ])} | 119 | ])} |
120 | onClick={onClick} | 120 | onClick={onClick} |
121 | onContextMenu={() => | 121 | onContextMenu={() => onContextMenuEditClick && contextMenu.popup()} |
122 | onContextMenuEditClick && contextMenu.popup() | ||
123 | } | ||
124 | data-tip={`${ | 122 | data-tip={`${ |
125 | shortcutIndex <= 9 | 123 | shortcutIndex <= 9 |
126 | ? `(${cmdOrCtrlShortcutKey(false)}+${altKey( | 124 | ? `(${cmdOrCtrlShortcutKey(false)}+${altKey( |
127 | false, | 125 | false, |
128 | )}+${shortcutIndex})` | 126 | )}+${shortcutIndex})` |
129 | : '' | 127 | : '' |
130 | }`} | 128 | }`} |
131 | > | 129 | > |
diff --git a/src/features/workspaces/components/WorkspaceItem.js b/src/features/workspaces/components/WorkspaceItem.tsx index ec7b19add..6fb02d2f5 100644 --- a/src/features/workspaces/components/WorkspaceItem.js +++ b/src/features/workspaces/components/WorkspaceItem.tsx | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
@@ -16,9 +16,15 @@ const styles = theme => ({ | |||
16 | columnName: {}, | 16 | columnName: {}, |
17 | }); | 17 | }); |
18 | 18 | ||
19 | type Props = { | ||
20 | classes: any; | ||
21 | workspace: any; | ||
22 | onItemClick: (workspace) => void; | ||
23 | }; | ||
24 | |||
19 | @injectSheet(styles) | 25 | @injectSheet(styles) |
20 | @observer | 26 | @observer |
21 | class WorkspaceItem extends Component { | 27 | class WorkspaceItem extends Component<Props> { |
22 | static propTypes = { | 28 | static propTypes = { |
23 | classes: PropTypes.object.isRequired, | 29 | classes: PropTypes.object.isRequired, |
24 | workspace: PropTypes.instanceOf(Workspace).isRequired, | 30 | workspace: PropTypes.instanceOf(Workspace).isRequired, |
diff --git a/src/features/workspaces/components/WorkspaceServiceListItem.js b/src/features/workspaces/components/WorkspaceServiceListItem.tsx index f6e2a2786..6e012eb1e 100644 --- a/src/features/workspaces/components/WorkspaceServiceListItem.js +++ b/src/features/workspaces/components/WorkspaceServiceListItem.tsx | |||
@@ -1,14 +1,12 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | ||
3 | import { observer } from 'mobx-react'; | 2 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 3 | import injectSheet from 'react-jss'; |
5 | import classnames from 'classnames'; | 4 | import classnames from 'classnames'; |
6 | import { Toggle } from '@meetfranz/forms'; | ||
7 | 5 | ||
8 | import Service from '../../../models/Service'; | 6 | import { Toggle } from '../../../components/ui/toggle/index'; |
9 | import ServiceIcon from '../../../components/ui/ServiceIcon'; | 7 | import ServiceIcon from '../../../components/ui/ServiceIcon'; |
10 | 8 | ||
11 | const styles = (theme) => ({ | 9 | const styles = theme => ({ |
12 | listItem: { | 10 | listItem: { |
13 | height: theme.workspaces.settings.listItems.height, | 11 | height: theme.workspaces.settings.listItems.height, |
14 | borderBottom: `1px solid ${theme.workspaces.settings.listItems.borderColor}`, | 12 | borderBottom: `1px solid ${theme.workspaces.settings.listItems.borderColor}`, |
@@ -31,29 +29,22 @@ const styles = (theme) => ({ | |||
31 | }, | 29 | }, |
32 | }); | 30 | }); |
33 | 31 | ||
34 | @injectSheet(styles) @observer | 32 | type Props = { |
35 | class WorkspaceServiceListItem extends Component { | 33 | classes: any; |
36 | static propTypes = { | 34 | isInWorkspace: boolean; |
37 | classes: PropTypes.object.isRequired, | 35 | onToggle: () => void; |
38 | isInWorkspace: PropTypes.bool.isRequired, | 36 | service: any; |
39 | onToggle: PropTypes.func.isRequired, | 37 | }; |
40 | service: PropTypes.instanceOf(Service).isRequired, | ||
41 | }; | ||
42 | 38 | ||
39 | @injectSheet(styles) | ||
40 | @observer | ||
41 | class WorkspaceServiceListItem extends Component<Props> { | ||
43 | render() { | 42 | render() { |
44 | const { | 43 | const { classes, isInWorkspace, onToggle, service } = this.props; |
45 | classes, | ||
46 | isInWorkspace, | ||
47 | onToggle, | ||
48 | service, | ||
49 | } = this.props; | ||
50 | 44 | ||
51 | return ( | 45 | return ( |
52 | <div className={classes.listItem}> | 46 | <div className={classes.listItem}> |
53 | <ServiceIcon | 47 | <ServiceIcon className={classes.serviceIcon} service={service} /> |
54 | className={classes.serviceIcon} | ||
55 | service={service} | ||
56 | /> | ||
57 | <span | 48 | <span |
58 | className={classnames([ | 49 | className={classnames([ |
59 | classes.label, | 50 | classes.label, |
diff --git a/src/features/workspaces/components/WorkspaceSwitchingIndicator.js b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js index 33a82cf4b..ff4e9475a 100644 --- a/src/features/workspaces/components/WorkspaceSwitchingIndicator.js +++ b/src/features/workspaces/components/WorkspaceSwitchingIndicator.js | |||
@@ -1,11 +1,11 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer } from 'mobx-react'; | 3 | import { observer } from 'mobx-react'; |
4 | import injectSheet from 'react-jss'; | 4 | import injectSheet from 'react-jss'; |
5 | import classnames from 'classnames'; | 5 | import classnames from 'classnames'; |
6 | import { Loader } from '@meetfranz/ui'; | ||
7 | import { defineMessages, injectIntl } from 'react-intl'; | 6 | import { defineMessages, injectIntl } from 'react-intl'; |
8 | 7 | ||
8 | import { Loader } from '../../../components/ui/loader/index'; | ||
9 | import { workspaceStore } from '../index'; | 9 | import { workspaceStore } from '../index'; |
10 | 10 | ||
11 | const messages = defineMessages({ | 11 | const messages = defineMessages({ |
diff --git a/src/features/workspaces/components/WorkspacesDashboard.js b/src/features/workspaces/components/WorkspacesDashboard.js index 49552df6b..6e0d98ffb 100644 --- a/src/features/workspaces/components/WorkspacesDashboard.js +++ b/src/features/workspaces/components/WorkspacesDashboard.js | |||
@@ -1,11 +1,12 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; | 3 | import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react'; |
4 | import { defineMessages, injectIntl } from 'react-intl'; | 4 | import { defineMessages, injectIntl } from 'react-intl'; |
5 | import injectSheet from 'react-jss'; | 5 | import injectSheet from 'react-jss'; |
6 | import { Infobox } from '@meetfranz/ui'; | ||
7 | 6 | ||
8 | import { mdiCheckboxMarkedCircleOutline } from '@mdi/js'; | 7 | import { mdiCheckboxMarkedCircleOutline } from '@mdi/js'; |
8 | |||
9 | import { Infobox } from '../../../components/ui/infobox/index'; | ||
9 | import Loader from '../../../components/ui/Loader'; | 10 | import Loader from '../../../components/ui/Loader'; |
10 | import WorkspaceItem from './WorkspaceItem'; | 11 | import WorkspaceItem from './WorkspaceItem'; |
11 | import CreateWorkspaceForm from './CreateWorkspaceForm'; | 12 | import CreateWorkspaceForm from './CreateWorkspaceForm'; |
@@ -40,7 +41,8 @@ const messages = defineMessages({ | |||
40 | }, | 41 | }, |
41 | workspaceFeatureInfo: { | 42 | workspaceFeatureInfo: { |
42 | id: 'settings.workspaces.workspaceFeatureInfo', | 43 | id: 'settings.workspaces.workspaceFeatureInfo', |
43 | defaultMessage: 'Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.', | 44 | defaultMessage: |
45 | 'Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.', | ||
44 | }, | 46 | }, |
45 | workspaceFeatureHeadline: { | 47 | workspaceFeatureHeadline: { |
46 | id: 'settings.workspaces.workspaceFeatureHeadline', | 48 | id: 'settings.workspaces.workspaceFeatureHeadline', |
diff --git a/src/features/workspaces/containers/EditWorkspaceScreen.js b/src/features/workspaces/containers/EditWorkspaceScreen.tsx index ba7606031..8e8f8179d 100644 --- a/src/features/workspaces/containers/EditWorkspaceScreen.js +++ b/src/features/workspaces/containers/EditWorkspaceScreen.tsx | |||
@@ -1,26 +1,26 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { inject, observer } from 'mobx-react'; | 2 | import { inject, observer } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | ||
4 | 3 | ||
5 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; | 4 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; |
6 | import EditWorkspaceForm from '../components/EditWorkspaceForm'; | 5 | import EditWorkspaceForm from '../components/EditWorkspaceForm'; |
7 | import ServicesStore from '../../../stores/ServicesStore'; | ||
8 | import Workspace from '../models/Workspace'; | 6 | import Workspace from '../models/Workspace'; |
9 | import { workspaceStore } from '../index'; | 7 | import { workspaceStore } from '../index'; |
10 | import { deleteWorkspaceRequest, updateWorkspaceRequest } from '../api'; | 8 | import { deleteWorkspaceRequest, updateWorkspaceRequest } from '../api'; |
11 | import WorkspacesStore from '../store'; | 9 | import { ServicesStore, WorkspacesStore } from '../../../stores.types'; |
12 | 10 | ||
13 | @inject('stores', 'actions') @observer | 11 | type Props = { |
14 | class EditWorkspaceScreen extends Component { | 12 | actions: { |
15 | static propTypes = { | 13 | workspaces: WorkspacesStore; |
16 | actions: PropTypes.shape({ | ||
17 | workspaces: PropTypes.instanceOf(WorkspacesStore), | ||
18 | }).isRequired, | ||
19 | stores: PropTypes.shape({ | ||
20 | services: PropTypes.instanceOf(ServicesStore).isRequired, | ||
21 | }).isRequired, | ||
22 | }; | 14 | }; |
15 | stores: { | ||
16 | services: ServicesStore; | ||
17 | }; | ||
18 | }; | ||
23 | 19 | ||
20 | @inject('stores', 'actions') | ||
21 | @observer | ||
22 | class EditWorkspaceScreen extends Component<Props> { | ||
23 | // @ts-expect-error Not all code paths return a value. | ||
24 | onDelete = () => { | 24 | onDelete = () => { |
25 | const { workspaceBeingEdited } = workspaceStore; | 25 | const { workspaceBeingEdited } = workspaceStore; |
26 | const { actions } = this.props; | 26 | const { actions } = this.props; |
@@ -28,12 +28,14 @@ class EditWorkspaceScreen extends Component { | |||
28 | actions.workspaces.delete({ workspace: workspaceBeingEdited }); | 28 | actions.workspaces.delete({ workspace: workspaceBeingEdited }); |
29 | }; | 29 | }; |
30 | 30 | ||
31 | onSave = (values) => { | 31 | onSave = values => { |
32 | const { workspaceBeingEdited } = workspaceStore; | 32 | const { workspaceBeingEdited } = workspaceStore; |
33 | const { actions } = this.props; | 33 | const { actions } = this.props; |
34 | const workspace = new Workspace( | 34 | const workspace = new Workspace({ |
35 | ({ saving: true, ...workspaceBeingEdited, ...values }), | 35 | saving: true, |
36 | ); | 36 | ...workspaceBeingEdited, |
37 | ...values, | ||
38 | }); | ||
37 | actions.workspaces.update({ workspace }); | 39 | actions.workspaces.update({ workspace }); |
38 | }; | 40 | }; |
39 | 41 | ||
diff --git a/src/features/workspaces/containers/WorkspacesScreen.js b/src/features/workspaces/containers/WorkspacesScreen.tsx index 4828658f9..a07e92439 100644 --- a/src/features/workspaces/containers/WorkspacesScreen.js +++ b/src/features/workspaces/containers/WorkspacesScreen.tsx | |||
@@ -1,6 +1,5 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import { inject, observer } from 'mobx-react'; | 2 | import { inject, observer } from 'mobx-react'; |
3 | import PropTypes from 'prop-types'; | ||
4 | import WorkspacesDashboard from '../components/WorkspacesDashboard'; | 3 | import WorkspacesDashboard from '../components/WorkspacesDashboard'; |
5 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; | 4 | import ErrorBoundary from '../../../components/util/ErrorBoundary'; |
6 | import { workspaceStore } from '../index'; | 5 | import { workspaceStore } from '../index'; |
@@ -10,16 +9,17 @@ import { | |||
10 | getUserWorkspacesRequest, | 9 | getUserWorkspacesRequest, |
11 | updateWorkspaceRequest, | 10 | updateWorkspaceRequest, |
12 | } from '../api'; | 11 | } from '../api'; |
13 | import WorkspacesStore from '../store'; | 12 | import { WorkspacesStore } from '../../../stores.types'; |
14 | 13 | ||
15 | @inject('stores', 'actions') @observer | 14 | type Props = { |
16 | class WorkspacesScreen extends Component { | 15 | actions: { |
17 | static propTypes = { | 16 | workspaces: WorkspacesStore; |
18 | actions: PropTypes.shape({ | ||
19 | workspaces: PropTypes.instanceOf(WorkspacesStore), | ||
20 | }).isRequired, | ||
21 | }; | 17 | }; |
18 | }; | ||
22 | 19 | ||
20 | @inject('stores', 'actions') | ||
21 | @observer | ||
22 | class WorkspacesScreen extends Component<Props> { | ||
23 | render() { | 23 | render() { |
24 | const { actions } = this.props; | 24 | const { actions } = this.props; |
25 | return ( | 25 | return ( |
@@ -30,8 +30,8 @@ class WorkspacesScreen extends Component { | |||
30 | createWorkspaceRequest={createWorkspaceRequest} | 30 | createWorkspaceRequest={createWorkspaceRequest} |
31 | deleteWorkspaceRequest={deleteWorkspaceRequest} | 31 | deleteWorkspaceRequest={deleteWorkspaceRequest} |
32 | updateWorkspaceRequest={updateWorkspaceRequest} | 32 | updateWorkspaceRequest={updateWorkspaceRequest} |
33 | onCreateWorkspaceSubmit={(data) => actions.workspaces.create(data)} | 33 | onCreateWorkspaceSubmit={data => actions.workspaces.create(data)} |
34 | onWorkspaceClick={(w) => actions.workspaces.edit({ workspace: w })} | 34 | onWorkspaceClick={w => actions.workspaces.edit({ workspace: w })} |
35 | /> | 35 | /> |
36 | </ErrorBoundary> | 36 | </ErrorBoundary> |
37 | ); | 37 | ); |
diff --git a/src/features/workspaces/store.js b/src/features/workspaces/store.js index db2b69f99..0fa43b723 100644 --- a/src/features/workspaces/store.js +++ b/src/features/workspaces/store.js | |||
@@ -124,7 +124,7 @@ export default class WorkspacesStore extends FeatureStore { | |||
124 | this.isFeatureActive = false; | 124 | this.isFeatureActive = false; |
125 | } | 125 | } |
126 | 126 | ||
127 | filterServicesByActiveWorkspace = (services) => { | 127 | filterServicesByActiveWorkspace = services => { |
128 | const { activeWorkspace, isFeatureActive } = this; | 128 | const { activeWorkspace, isFeatureActive } = this; |
129 | if (isFeatureActive && activeWorkspace) { | 129 | if (isFeatureActive && activeWorkspace) { |
130 | return this.getWorkspaceServices(activeWorkspace); | 130 | return this.getWorkspaceServices(activeWorkspace); |
@@ -134,14 +134,14 @@ export default class WorkspacesStore extends FeatureStore { | |||
134 | 134 | ||
135 | getWorkspaceServices(workspace) { | 135 | getWorkspaceServices(workspace) { |
136 | const { services } = this.stores; | 136 | const { services } = this.stores; |
137 | return workspace.services.map((id) => services.one(id)).filter((s) => !!s); | 137 | return workspace.services.map(id => services.one(id)).filter(s => !!s); |
138 | } | 138 | } |
139 | 139 | ||
140 | // ========== PRIVATE METHODS ========= // | 140 | // ========== PRIVATE METHODS ========= // |
141 | 141 | ||
142 | _getWorkspaceById = (id) => this.workspaces.find((w) => w.id === id); | 142 | _getWorkspaceById = id => this.workspaces.find(w => w.id === id); |
143 | 143 | ||
144 | _updateSettings = (changes) => { | 144 | _updateSettings = changes => { |
145 | localStorage.setItem('workspaces', { | 145 | localStorage.setItem('workspaces', { |
146 | ...this.settings, | 146 | ...this.settings, |
147 | ...changes, | 147 | ...changes, |
@@ -191,9 +191,15 @@ export default class WorkspacesStore extends FeatureStore { | |||
191 | this.isSwitchingWorkspace = false; | 191 | this.isSwitchingWorkspace = false; |
192 | this.nextWorkspace = null; | 192 | this.nextWorkspace = null; |
193 | if (this.stores.settings.app.splitMode) { | 193 | if (this.stores.settings.app.splitMode) { |
194 | const serviceNames = new Set(this.getWorkspaceServices(workspace).map(service => service.name)); | 194 | const serviceNames = new Set( |
195 | for (const wrapper of document.querySelectorAll('.services__webview-wrapper')) { | 195 | this.getWorkspaceServices(workspace).map(service => service.name), |
196 | wrapper.style.display = serviceNames.has(wrapper.dataset.name) ? '' : 'none'; | 196 | ); |
197 | for (const wrapper of document.querySelectorAll( | ||
198 | '.services__webview-wrapper', | ||
199 | )) { | ||
200 | wrapper.style.display = serviceNames.has(wrapper.dataset.name) | ||
201 | ? '' | ||
202 | : 'none'; | ||
197 | } | 203 | } |
198 | } | 204 | } |
199 | }, 1000); | 205 | }, 1000); |
@@ -212,7 +218,9 @@ export default class WorkspacesStore extends FeatureStore { | |||
212 | setTimeout(() => { | 218 | setTimeout(() => { |
213 | this.isSwitchingWorkspace = false; | 219 | this.isSwitchingWorkspace = false; |
214 | if (this.stores.settings.app.splitMode) { | 220 | if (this.stores.settings.app.splitMode) { |
215 | for (const wrapper of document.querySelectorAll('.services__webview-wrapper')) { | 221 | for (const wrapper of document.querySelectorAll( |
222 | '.services__webview-wrapper', | ||
223 | )) { | ||
216 | wrapper.style.display = ''; | 224 | wrapper.style.display = ''; |
217 | } | 225 | } |
218 | } | 226 | } |
@@ -262,7 +270,8 @@ export default class WorkspacesStore extends FeatureStore { | |||
262 | const activeService = this.stores.services.active; | 270 | const activeService = this.stores.services.active; |
263 | const workspaceServices = this.getWorkspaceServices(this.activeWorkspace); | 271 | const workspaceServices = this.getWorkspaceServices(this.activeWorkspace); |
264 | if (workspaceServices.length <= 0) return; | 272 | if (workspaceServices.length <= 0) return; |
265 | const isActiveServiceInWorkspace = workspaceServices.includes(activeService); | 273 | const isActiveServiceInWorkspace = |
274 | workspaceServices.includes(activeService); | ||
266 | if (!isActiveServiceInWorkspace) { | 275 | if (!isActiveServiceInWorkspace) { |
267 | this.actions.service.setActive({ | 276 | this.actions.service.setActive({ |
268 | serviceId: workspaceServices[0].id, | 277 | serviceId: workspaceServices[0].id, |
@@ -288,8 +297,10 @@ export default class WorkspacesStore extends FeatureStore { | |||
288 | const isWorkspaceSettingsRoute = router.location.pathname.includes( | 297 | const isWorkspaceSettingsRoute = router.location.pathname.includes( |
289 | WORKSPACES_ROUTES.ROOT, | 298 | WORKSPACES_ROUTES.ROOT, |
290 | ); | 299 | ); |
291 | const isSwitchingToSettingsRoute = !this.isSettingsRouteActive && isWorkspaceSettingsRoute; | 300 | const isSwitchingToSettingsRoute = |
292 | const isLeavingSettingsRoute = !isWorkspaceSettingsRoute && this.isSettingsRouteActive; | 301 | !this.isSettingsRouteActive && isWorkspaceSettingsRoute; |
302 | const isLeavingSettingsRoute = | ||
303 | !isWorkspaceSettingsRoute && this.isSettingsRouteActive; | ||
293 | 304 | ||
294 | if (isSwitchingToSettingsRoute) { | 305 | if (isSwitchingToSettingsRoute) { |
295 | this.isSettingsRouteActive = true; | 306 | this.isSettingsRouteActive = true; |
@@ -300,8 +311,8 @@ export default class WorkspacesStore extends FeatureStore { | |||
300 | } else if (isLeavingSettingsRoute) { | 311 | } else if (isLeavingSettingsRoute) { |
301 | this.isSettingsRouteActive = false; | 312 | this.isSettingsRouteActive = false; |
302 | if ( | 313 | if ( |
303 | !this._wasDrawerOpenBeforeSettingsRoute | 314 | !this._wasDrawerOpenBeforeSettingsRoute && |
304 | && this.isWorkspaceDrawerOpen | 315 | this.isWorkspaceDrawerOpen |
305 | ) { | 316 | ) { |
306 | workspaceActions.toggleWorkspaceDrawer(); | 317 | workspaceActions.toggleWorkspaceDrawer(); |
307 | } | 318 | } |
@@ -311,14 +322,15 @@ export default class WorkspacesStore extends FeatureStore { | |||
311 | _cleanupInvalidServiceReferences = () => { | 322 | _cleanupInvalidServiceReferences = () => { |
312 | const { services } = this.stores; | 323 | const { services } = this.stores; |
313 | const { allServicesRequest } = services; | 324 | const { allServicesRequest } = services; |
314 | const servicesHaveBeenLoaded = allServicesRequest.wasExecuted && !allServicesRequest.isError; | 325 | const servicesHaveBeenLoaded = |
326 | allServicesRequest.wasExecuted && !allServicesRequest.isError; | ||
315 | // Loop through all workspaces and remove invalid service ids (locally) | 327 | // Loop through all workspaces and remove invalid service ids (locally) |
316 | for (const workspace of this.workspaces) { | 328 | for (const workspace of this.workspaces) { |
317 | for (const serviceId of workspace.services) { | 329 | for (const serviceId of workspace.services) { |
318 | if ( | 330 | if ( |
319 | servicesHaveBeenLoaded | 331 | servicesHaveBeenLoaded && |
320 | && !services.one(serviceId) | 332 | !services.one(serviceId) && |
321 | && serviceId !== KEEP_WS_LOADED_USID | 333 | serviceId !== KEEP_WS_LOADED_USID |
322 | ) { | 334 | ) { |
323 | workspace.services.remove(serviceId); | 335 | workspace.services.remove(serviceId); |
324 | } | 336 | } |
diff --git a/src/helpers/array-helpers.ts b/src/helpers/array-helpers.ts index ae5d8d99f..3f8806176 100644 --- a/src/helpers/array-helpers.ts +++ b/src/helpers/array-helpers.ts | |||
@@ -1,4 +1,5 @@ | |||
1 | export const shuffleArray = (arr: any[]) => arr | 1 | export const shuffleArray = (arr: any[]) => |
2 | .map((a) => [Math.random(), a]) | 2 | arr |
3 | .sort((a, b) => a[0] - b[0]) | 3 | .map(a => [Math.random(), a]) |
4 | .map((a) => a[1]); | 4 | .sort((a, b) => a[0] - b[0]) |
5 | .map(a => a[1]); | ||
diff --git a/src/helpers/async-helpers.ts b/src/helpers/async-helpers.ts index aae3c3928..6b1f24b5a 100644 --- a/src/helpers/async-helpers.ts +++ b/src/helpers/async-helpers.ts | |||
@@ -1,5 +1,3 @@ | |||
1 | /* eslint-disable import/prefer-default-export */ | ||
2 | |||
3 | export function sleep(ms: number = 0) { | 1 | export function sleep(ms: number = 0) { |
4 | return new Promise((r) => setTimeout(r, ms)); | 2 | return new Promise(r => setTimeout(r, ms)); |
5 | } | 3 | } |
diff --git a/src/helpers/i18n-helpers.ts b/src/helpers/i18n-helpers.ts index ec7dc8e98..e6c66c7d3 100644 --- a/src/helpers/i18n-helpers.ts +++ b/src/helpers/i18n-helpers.ts | |||
@@ -1,52 +1,44 @@ | |||
1 | export function getLocale({ | 1 | export function getLocale({ locale, locales, fallbackLocale }) { |
2 | locale, locales, defaultLocale, fallbackLocale, | 2 | if (!locale) { |
3 | }) { | 3 | return fallbackLocale; |
4 | let localeStr = locale; | 4 | } |
5 | if (locales[locale] === undefined) { | 5 | |
6 | if (!locales[locale]) { | ||
6 | let localeFuzzy: string | undefined; | 7 | let localeFuzzy: string | undefined; |
7 | for (const localStr of Object.keys(locales)) { | 8 | for (const localStr of Object.keys(locales)) { |
8 | if (locales && Object.hasOwnProperty.call(locales, localStr) && locale.slice(0, 2) === localStr.slice(0, 2)) { | 9 | if (locale.slice(0, 2) === localStr.slice(0, 2)) { |
9 | localeFuzzy = localStr; | 10 | localeFuzzy = localStr; |
10 | } | 11 | } |
11 | } | 12 | } |
12 | 13 | ||
13 | if (localeFuzzy !== undefined) { | 14 | if (localeFuzzy) { |
14 | localeStr = localeFuzzy; | 15 | return localeFuzzy; |
15 | } | 16 | } |
16 | } | 17 | } |
17 | 18 | ||
18 | if (locales[localeStr] === undefined) { | 19 | return locale; |
19 | localeStr = defaultLocale; | ||
20 | } | ||
21 | |||
22 | if (!localeStr) { | ||
23 | localeStr = fallbackLocale; | ||
24 | } | ||
25 | |||
26 | return localeStr; | ||
27 | } | 20 | } |
28 | 21 | ||
29 | export function getSelectOptions({ | 22 | export function getSelectOptions({ |
30 | locales, resetToDefaultText = '', automaticDetectionText = '', sort = true, | 23 | locales, |
24 | resetToDefaultText = '', | ||
25 | automaticDetectionText = '', | ||
26 | sort = true, | ||
31 | }) { | 27 | }) { |
32 | const options: object[] = []; | 28 | const options: object[] = []; |
33 | 29 | ||
34 | if (resetToDefaultText) { | 30 | if (resetToDefaultText) { |
35 | options.push( | 31 | options.push({ |
36 | { | 32 | value: '', |
37 | value: '', | 33 | label: resetToDefaultText, |
38 | label: resetToDefaultText, | 34 | }); |
39 | }, | ||
40 | ); | ||
41 | } | 35 | } |
42 | 36 | ||
43 | if (automaticDetectionText) { | 37 | if (automaticDetectionText) { |
44 | options.push( | 38 | options.push({ |
45 | { | 39 | value: 'automatic', |
46 | value: 'automatic', | 40 | label: automaticDetectionText, |
47 | label: automaticDetectionText, | 41 | }); |
48 | }, | ||
49 | ); | ||
50 | } | 42 | } |
51 | 43 | ||
52 | options.push({ | 44 | options.push({ |
diff --git a/src/helpers/password-helpers.ts b/src/helpers/password-helpers.ts index e5d9a4a25..053321bbf 100644 --- a/src/helpers/password-helpers.ts +++ b/src/helpers/password-helpers.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import crypto from 'crypto'; | 1 | import { createHash, BinaryLike } from 'crypto'; |
2 | 2 | ||
3 | export function hash(password: crypto.BinaryLike) { | 3 | export function hash(password: BinaryLike) { |
4 | return crypto.createHash('sha256').update(password).digest('base64'); | 4 | return createHash('sha256').update(password).digest('base64'); |
5 | } | 5 | } |
6 | 6 | ||
7 | export function scorePassword(password: string) { | 7 | export function scorePassword(password: string) { |
diff --git a/src/helpers/routing-helpers.ts b/src/helpers/routing-helpers.ts index 18169f01b..46895aa6b 100644 --- a/src/helpers/routing-helpers.ts +++ b/src/helpers/routing-helpers.ts | |||
@@ -1,3 +1,4 @@ | |||
1 | import RouteParser from 'route-parser'; | 1 | import RouteParser from 'route-parser'; |
2 | 2 | ||
3 | export const matchRoute = (pattern: string, path: string) => new RouteParser(pattern).match(path); | 3 | export const matchRoute = (pattern: string, path: string) => |
4 | new RouteParser(pattern).match(path); | ||
diff --git a/src/helpers/schedule-helpers.ts b/src/helpers/schedule-helpers.ts index 55b7c1e6f..37caffd79 100644 --- a/src/helpers/schedule-helpers.ts +++ b/src/helpers/schedule-helpers.ts | |||
@@ -1,17 +1,9 @@ | |||
1 | /* eslint-disable import/prefer-default-export */ | ||
2 | |||
3 | export function isInTimeframe(start: string, end: string) { | 1 | export function isInTimeframe(start: string, end: string) { |
4 | const [ | 2 | const [startHourStr, startMinuteStr] = start.split(':'); |
5 | startHourStr, | ||
6 | startMinuteStr, | ||
7 | ] = start.split(':'); | ||
8 | const startHour = Number.parseInt(startHourStr, 10); | 3 | const startHour = Number.parseInt(startHourStr, 10); |
9 | const startMinute = Number.parseInt(startMinuteStr, 10); | 4 | const startMinute = Number.parseInt(startMinuteStr, 10); |
10 | 5 | ||
11 | const [ | 6 | const [endHourStr, endMinuteStr] = end.split(':'); |
12 | endHourStr, | ||
13 | endMinuteStr, | ||
14 | ] = end.split(':'); | ||
15 | const endHour = Number.parseInt(endHourStr, 10); | 7 | const endHour = Number.parseInt(endHourStr, 10); |
16 | const endMinute = Number.parseInt(endMinuteStr, 10); | 8 | const endMinute = Number.parseInt(endMinuteStr, 10); |
17 | 9 | ||
@@ -20,46 +12,31 @@ export function isInTimeframe(start: string, end: string) { | |||
20 | 12 | ||
21 | // Check if the end time is before the start time (scheduled overnight) | 13 | // Check if the end time is before the start time (scheduled overnight) |
22 | // as we need to change our checks based on this | 14 | // as we need to change our checks based on this |
23 | const endBeforeStart = (startHour > endHour || (startHour === endHour && startMinute > endMinute)); | 15 | const endBeforeStart = |
16 | startHour > endHour || (startHour === endHour && startMinute > endMinute); | ||
24 | 17 | ||
25 | if ( | 18 | if ( |
26 | // End is after start (e.g. 09:00-17:00) | 19 | // End is after start (e.g. 09:00-17:00) |
27 | !endBeforeStart | 20 | !endBeforeStart && |
28 | // Check if past start | 21 | // Check if past start |
29 | && ((currentHour > startHour | 22 | (currentHour > startHour || |
30 | || ( | 23 | (currentHour === startHour && currentMinute >= startMinute)) && |
31 | currentHour === startHour | 24 | // Check that not past end |
32 | && currentMinute >= startMinute | 25 | (currentHour < endHour || |
33 | ) | 26 | (currentHour === endHour && currentMinute < endMinute)) |
34 | ) | ||
35 | // Check that not past end | ||
36 | && (currentHour < endHour | ||
37 | || ( | ||
38 | currentHour === endHour | ||
39 | && currentMinute < endMinute | ||
40 | ) | ||
41 | )) | ||
42 | ) { | 27 | ) { |
43 | // We are in scheduled timeframe | 28 | // We are in scheduled timeframe |
44 | return true; | 29 | return true; |
45 | } | 30 | } |
46 | if ( | 31 | if ( |
47 | // End is before start (e.g. 17:00-09:00) | 32 | // End is before start (e.g. 17:00-09:00) |
48 | endBeforeStart | 33 | endBeforeStart && |
49 | // Check if past start | 34 | // Check if past start |
50 | && ((currentHour > startHour | 35 | (currentHour > startHour || |
51 | || ( | 36 | (currentHour === startHour && currentMinute >= startMinute) || |
52 | currentHour === startHour | ||
53 | && currentMinute >= startMinute | ||
54 | ) | ||
55 | ) | ||
56 | // Check that we are not past end | 37 | // Check that we are not past end |
57 | || (currentHour < endHour | 38 | currentHour < endHour || |
58 | || ( | 39 | (currentHour === endHour && currentMinute < endMinute)) |
59 | currentHour === endHour | ||
60 | && currentMinute < endMinute | ||
61 | ) | ||
62 | )) | ||
63 | ) { | 40 | ) { |
64 | // We are also in scheduled timeframe | 41 | // We are also in scheduled timeframe |
65 | return true; | 42 | return true; |
diff --git a/src/helpers/url-helpers.ts b/src/helpers/url-helpers.ts index 1e87ecabb..135f06cbf 100644 --- a/src/helpers/url-helpers.ts +++ b/src/helpers/url-helpers.ts | |||
@@ -29,7 +29,10 @@ export async function openPath(folderName: string) { | |||
29 | } | 29 | } |
30 | 30 | ||
31 | // TODO: Need to verify and fix/remove the skipping logic. Ideally, we should never skip this check | 31 | // TODO: Need to verify and fix/remove the skipping logic. Ideally, we should never skip this check |
32 | export function openExternalUrl(url: string | URL, skipValidityCheck: boolean = false) { | 32 | export function openExternalUrl( |
33 | url: string | URL, | ||
34 | skipValidityCheck: boolean = false, | ||
35 | ) { | ||
33 | debug('Open url:', url, 'with skipValidityCheck:', skipValidityCheck); | 36 | debug('Open url:', url, 'with skipValidityCheck:', skipValidityCheck); |
34 | if (skipValidityCheck || isValidExternalURL(url)) { | 37 | if (skipValidityCheck || isValidExternalURL(url)) { |
35 | shell.openExternal(url.toString()); | 38 | shell.openExternal(url.toString()); |
diff --git a/src/helpers/userAgent-helpers.ts b/src/helpers/userAgent-helpers.ts index 091a76400..c1e8e02da 100644 --- a/src/helpers/userAgent-helpers.ts +++ b/src/helpers/userAgent-helpers.ts | |||
@@ -1,13 +1,18 @@ | |||
1 | import os from 'os'; | 1 | import { cpus } from 'os'; |
2 | import macosVersion from 'macos-version'; | 2 | import macosVersion from 'macos-version'; |
3 | import { chrome } from 'useragent-generator'; | 3 | import { chrome } from 'useragent-generator'; |
4 | import { | 4 | import { |
5 | chromeVersion, isMac, isWindows, is64Bit, osArch, osRelease, | 5 | chromeVersion, |
6 | isMac, | ||
7 | isWindows, | ||
8 | is64Bit, | ||
9 | osArch, | ||
10 | osRelease, | ||
6 | } from '../environment'; | 11 | } from '../environment'; |
7 | 12 | ||
8 | function macOS() { | 13 | function macOS() { |
9 | const version = macosVersion() || ''; | 14 | const version = macosVersion() || ''; |
10 | let cpuName = os.cpus()[0].model.split(' ')[0]; | 15 | let cpuName = cpus()[0].model.split(' ')[0]; |
11 | if (cpuName && /\(/.test(cpuName)) { | 16 | if (cpuName && /\(/.test(cpuName)) { |
12 | cpuName = cpuName.split('(')[0]; | 17 | cpuName = cpuName.split('(')[0]; |
13 | } | 18 | } |
diff --git a/src/i18n/apply-branding.ts b/src/i18n/apply-branding.ts index 801a4a525..7943c099d 100644 --- a/src/i18n/apply-branding.ts +++ b/src/i18n/apply-branding.ts | |||
@@ -1,8 +1,8 @@ | |||
1 | /** | 1 | /** |
2 | * Apply Ferdi branding to i18n translations | 2 | * Apply Ferdi branding to i18n translations |
3 | */ | 3 | */ |
4 | import fs from 'fs-extra'; | 4 | import { readdirSync, readJson, writeJson } from 'fs-extra'; |
5 | import path from 'path'; | 5 | import { join } from 'path'; |
6 | 6 | ||
7 | console.log('Applying Ferdi branding to translations...'); | 7 | console.log('Applying Ferdi branding to translations...'); |
8 | 8 | ||
@@ -31,13 +31,13 @@ const replace = { | |||
31 | '!!!': '', | 31 | '!!!': '', |
32 | }; | 32 | }; |
33 | 33 | ||
34 | const locales = path.join(__dirname, 'locales'); | 34 | const locales = join(__dirname, 'locales'); |
35 | const files = fs.readdirSync(locales); | 35 | const files = readdirSync(locales); |
36 | 36 | ||
37 | const replaceFind = Object.keys(replace); | 37 | const replaceFind = Object.keys(replace); |
38 | const replaceReplaceWith = Object.values(replace); | 38 | const replaceReplaceWith = Object.values(replace); |
39 | 39 | ||
40 | const replaceStr = (str, find, replaceWith) => { | 40 | const replaceStr = (str: string, find: any[], replaceWith: string[]) => { |
41 | for (const [i, element] of find.entries()) { | 41 | for (const [i, element] of find.entries()) { |
42 | str = str.replace(new RegExp(element, 'gi'), replaceWith[i]); | 42 | str = str.replace(new RegExp(element, 'gi'), replaceWith[i]); |
43 | } | 43 | } |
@@ -49,8 +49,8 @@ files.forEach(async file => { | |||
49 | if (ignoreFiles.has(file)) return; | 49 | if (ignoreFiles.has(file)) return; |
50 | 50 | ||
51 | // Read locale data | 51 | // Read locale data |
52 | const filePath = path.join(locales, file); | 52 | const filePath = join(locales, file); |
53 | const locale = await fs.readJson(filePath); | 53 | const locale = await readJson(filePath); |
54 | 54 | ||
55 | // Replace branding | 55 | // Replace branding |
56 | for (const key in locale) { | 56 | for (const key in locale) { |
@@ -59,7 +59,7 @@ files.forEach(async file => { | |||
59 | } | 59 | } |
60 | } | 60 | } |
61 | 61 | ||
62 | await fs.writeJson(filePath, locale, { | 62 | await writeJson(filePath, locale, { |
63 | spaces: 2, | 63 | spaces: 2, |
64 | EOL: '\n', | 64 | EOL: '\n', |
65 | }); | 65 | }); |
diff --git a/src/i18n/locales/af.json b/src/i18n/locales/af.json index 041b02632..ff10978c4 100644 --- a/src/i18n/locales/af.json +++ b/src/i18n/locales/af.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/ar.json b/src/i18n/locales/ar.json index 2ab80e865..8183d193f 100644 --- a/src/i18n/locales/ar.json +++ b/src/i18n/locales/ar.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "تمكين الوضع المظلم", | 211 | "settings.app.form.darkMode": "تمكين الوضع المظلم", |
212 | "settings.app.form.enableGPUAcceleration": "تÙعيل التسريع بوØدة معالجة الرسومات", | 212 | "settings.app.form.enableGPUAcceleration": "تÙعيل التسريع بوØدة معالجة الرسومات", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "تÙعيل القÙÙ„ بكلمة المرور", | 214 | "settings.app.form.enableLock": "تÙعيل القÙÙ„ بكلمة المرور", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "تÙعيل التصØÙŠØ Ø§Ù„Ø¥Ù…Ù„Ø§Ø¦ÙŠ", | 217 | "settings.app.form.enableSpellchecking": "تÙعيل التصØÙŠØ Ø§Ù„Ø¥Ù…Ù„Ø§Ø¦ÙŠ", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "تÙعيل الإشعارات", | 312 | "settings.service.form.enableNotification": "تÙعيل الإشعارات", |
311 | "settings.service.form.enableService": "تÙعيل الخدمة", | 313 | "settings.service.form.enableService": "تÙعيل الخدمة", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "شارات الرسائل غير المقروءة", | 315 | "settings.service.form.headlineBadges": "شارات الرسائل غير المقروءة", |
313 | "settings.service.form.headlineDarkReaderSettings": "إعدادات وضع القارئ المظلم", | 316 | "settings.service.form.headlineDarkReaderSettings": "إعدادات وضع القارئ المظلم", |
314 | "settings.service.form.headlineGeneral": "عام", | 317 | "settings.service.form.headlineGeneral": "عام", |
diff --git a/src/i18n/locales/be.json b/src/i18n/locales/be.json index 8caf73fe6..5e5b594fe 100644 --- a/src/i18n/locales/be.json +++ b/src/i18n/locales/be.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/bs.json b/src/i18n/locales/bs.json index 041b02632..ff10978c4 100644 --- a/src/i18n/locales/bs.json +++ b/src/i18n/locales/bs.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/ca.json b/src/i18n/locales/ca.json index 6e33be919..b04591bee 100644 --- a/src/i18n/locales/ca.json +++ b/src/i18n/locales/ca.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Activar el Mode Fosc", | 211 | "settings.app.form.darkMode": "Activar el Mode Fosc", |
212 | "settings.app.form.enableGPUAcceleration": "Activar acceleració GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Activar acceleració GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Habilita la comprobació ortogrà fica", | 217 | "settings.app.form.enableSpellchecking": "Habilita la comprobació ortogrà fica", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Activa les notificacions", | 312 | "settings.service.form.enableNotification": "Activa les notificacions", |
311 | "settings.service.form.enableService": "Activa el servei", | 313 | "settings.service.form.enableService": "Activa el servei", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "InsÃgnies de missatges no llegits", | 315 | "settings.service.form.headlineBadges": "InsÃgnies de missatges no llegits", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/cs.json b/src/i18n/locales/cs.json index 08468e307..edb840722 100644 --- a/src/i18n/locales/cs.json +++ b/src/i18n/locales/cs.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Povolit Tmavý vzhled", | 211 | "settings.app.form.darkMode": "Povolit Tmavý vzhled", |
212 | "settings.app.form.enableGPUAcceleration": "Aktivovat GPU zrychlenÃ", | 212 | "settings.app.form.enableGPUAcceleration": "Aktivovat GPU zrychlenÃ", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Zapnout kontrolu pravopisu", | 217 | "settings.app.form.enableSpellchecking": "Zapnout kontrolu pravopisu", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Povolit upozornÄ›nÃ", | 312 | "settings.service.form.enableNotification": "Povolit upozornÄ›nÃ", |
311 | "settings.service.form.enableService": "Povolit službu", | 313 | "settings.service.form.enableService": "Povolit službu", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Odznaky nepÅ™eÄtených zpráv", | 315 | "settings.service.form.headlineBadges": "Odznaky nepÅ™eÄtených zpráv", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Obecné", | 317 | "settings.service.form.headlineGeneral": "Obecné", |
diff --git a/src/i18n/locales/da.json b/src/i18n/locales/da.json index 9a6d0cf7e..fbe907f10 100644 --- a/src/i18n/locales/da.json +++ b/src/i18n/locales/da.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Aktiver mørk tilstand", | 211 | "settings.app.form.darkMode": "Aktiver mørk tilstand", |
212 | "settings.app.form.enableGPUAcceleration": "Aktiver GPU-acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Aktiver GPU-acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Aktiver adgangskodelås", | 214 | "settings.app.form.enableLock": "Aktiver adgangskodelås", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Aktiver stavekontrol", | 217 | "settings.app.form.enableSpellchecking": "Aktiver stavekontrol", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Aktiver notifikationer", | 312 | "settings.service.form.enableNotification": "Aktiver notifikationer", |
311 | "settings.service.form.enableService": "Aktiverede tjenester", | 313 | "settings.service.form.enableService": "Aktiverede tjenester", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Ulæste beskedbadges", | 315 | "settings.service.form.headlineBadges": "Ulæste beskedbadges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Mørk læser indstillinger", | 316 | "settings.service.form.headlineDarkReaderSettings": "Mørk læser indstillinger", |
314 | "settings.service.form.headlineGeneral": "Generelt", | 317 | "settings.service.form.headlineGeneral": "Generelt", |
diff --git a/src/i18n/locales/de.json b/src/i18n/locales/de.json index 0baa4cf5e..a5498fcc9 100644 --- a/src/i18n/locales/de.json +++ b/src/i18n/locales/de.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Dark Mode aktivieren", | 211 | "settings.app.form.darkMode": "Dark Mode aktivieren", |
212 | "settings.app.form.enableGPUAcceleration": "Hardwarebeschleunigung aktivieren", | 212 | "settings.app.form.enableGPUAcceleration": "Hardwarebeschleunigung aktivieren", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Passwort-Sperre aktivieren", | 214 | "settings.app.form.enableLock": "Passwort-Sperre aktivieren", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Ferdi immer in der Menüleiste anzeigen", | 216 | "settings.app.form.enableMenuBar": "Ferdi immer in der Menüleiste anzeigen", |
215 | "settings.app.form.enableSpellchecking": "Rechtschreibprüfung aktivieren", | 217 | "settings.app.form.enableSpellchecking": "Rechtschreibprüfung aktivieren", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Ruhezustand aktivieren", | 311 | "settings.service.form.enableHibernation": "Ruhezustand aktivieren", |
310 | "settings.service.form.enableNotification": "Benachrichtigungen aktivieren", | 312 | "settings.service.form.enableNotification": "Benachrichtigungen aktivieren", |
311 | "settings.service.form.enableService": "Dienst aktivieren", | 313 | "settings.service.form.enableService": "Dienst aktivieren", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Nachrichten-Badge", | 315 | "settings.service.form.headlineBadges": "Nachrichten-Badge", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Einstellungen", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Einstellungen", |
314 | "settings.service.form.headlineGeneral": "Allgemeines", | 317 | "settings.service.form.headlineGeneral": "Allgemeines", |
diff --git a/src/i18n/locales/el.json b/src/i18n/locales/el.json index 4c658297e..22a3a0bc5 100644 --- a/src/i18n/locales/el.json +++ b/src/i18n/locales/el.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "ΕνεÏγοποιήση Ενίσχυσης GPU ", | 212 | "settings.app.form.enableGPUAcceleration": "ΕνεÏγοποιήση Ενίσχυσης GPU ", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "ΕνεÏγοποίηση οÏθογÏÎ±Ï†Î¹ÎºÎ¿Ï ÎµÎ»Îγχου", | 217 | "settings.app.form.enableSpellchecking": "ΕνεÏγοποίηση οÏθογÏÎ±Ï†Î¹ÎºÎ¿Ï ÎµÎ»Îγχου", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "ΕνεÏγοποίηση ειδοποιήσεων", | 312 | "settings.service.form.enableNotification": "ΕνεÏγοποίηση ειδοποιήσεων", |
311 | "settings.service.form.enableService": "ΕνεÏγοποίηση υπηÏεσίας", | 313 | "settings.service.form.enableService": "ΕνεÏγοποίηση υπηÏεσίας", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Εικονίδια μη αναγνωσμÎνου μηνÏματος", | 315 | "settings.service.form.headlineBadges": "Εικονίδια μη αναγνωσμÎνου μηνÏματος", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Γενικά", | 317 | "settings.service.form.headlineGeneral": "Γενικά", |
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json index 812ec8c4c..d5cf072f6 100644 --- a/src/i18n/locales/en-US.json +++ b/src/i18n/locales/en-US.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/es.json b/src/i18n/locales/es.json index 518e862ba..8f3f05c2c 100644 --- a/src/i18n/locales/es.json +++ b/src/i18n/locales/es.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Servidor de TODO personalizado", | 210 | "settings.app.form.customTodoServer": "Servidor de TODO personalizado", |
211 | "settings.app.form.darkMode": "Habilitar modo oscuro", | 211 | "settings.app.form.darkMode": "Habilitar modo oscuro", |
212 | "settings.app.form.enableGPUAcceleration": "Habilitar aceleración de GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Habilitar aceleración de GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Habilitar acceso directo global para ocultar Ferdi", | ||
213 | "settings.app.form.enableLock": "Activar bloqueo por contraseña", | 214 | "settings.app.form.enableLock": "Activar bloqueo por contraseña", |
215 | "settings.app.form.enableLongPressServiceHint": "Habilitar sugerencia de acceso directo al servicio con pulsación larga", | ||
214 | "settings.app.form.enableMenuBar": "Mostrar a Ferdi en la barra de menús", | 216 | "settings.app.form.enableMenuBar": "Mostrar a Ferdi en la barra de menús", |
215 | "settings.app.form.enableSpellchecking": "Activar corrección ortográfica", | 217 | "settings.app.form.enableSpellchecking": "Activar corrección ortográfica", |
216 | "settings.app.form.enableSystemTray": "Mostrar siempre icono de Ferdi en la bandeja del sistema", | 218 | "settings.app.form.enableSystemTray": "Mostrar siempre icono de Ferdi en la bandeja del sistema", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Habilitar hibernación", | 311 | "settings.service.form.enableHibernation": "Habilitar hibernación", |
310 | "settings.service.form.enableNotification": "Activar notificaciones", | 312 | "settings.service.form.enableNotification": "Activar notificaciones", |
311 | "settings.service.form.enableService": "Activar servicio", | 313 | "settings.service.form.enableService": "Activar servicio", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Insignias de mensaje no leÃdos", | 315 | "settings.service.form.headlineBadges": "Insignias de mensaje no leÃdos", |
313 | "settings.service.form.headlineDarkReaderSettings": "Configuración del Lector Oscuro", | 316 | "settings.service.form.headlineDarkReaderSettings": "Configuración del Lector Oscuro", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/fi.json b/src/i18n/locales/fi.json index e6c756935..dbac3aa59 100644 --- a/src/i18n/locales/fi.json +++ b/src/i18n/locales/fi.json | |||
@@ -186,7 +186,7 @@ | |||
186 | "settings.account.tryReloadServices": "Yritä uudelleen", | 186 | "settings.account.tryReloadServices": "Yritä uudelleen", |
187 | "settings.account.tryReloadUserInfoRequest": "Yritä uudelleen", | 187 | "settings.account.tryReloadUserInfoRequest": "Yritä uudelleen", |
188 | "settings.account.userInfoRequestFailed": "Käyttäjätietoja ei voitu ladata", | 188 | "settings.account.userInfoRequestFailed": "Käyttäjätietoja ei voitu ladata", |
189 | "settings.account.yourLicense": "Your Ferdi License:", | 189 | "settings.account.yourLicense": "Ferdi-lisenssisi:", |
190 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: {defaultAccentColor})", | 190 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: {defaultAccentColor})", |
191 | "settings.app.buttonClearAllCache": "Tyhjennä välimuisti", | 191 | "settings.app.buttonClearAllCache": "Tyhjennä välimuisti", |
192 | "settings.app.buttonInstallUpdate": "Käynnistä uudelleen ja asenna päivitys", | 192 | "settings.app.buttonInstallUpdate": "Käynnistä uudelleen ja asenna päivitys", |
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Ota tumma tila käyttöön", | 211 | "settings.app.form.darkMode": "Ota tumma tila käyttöön", |
212 | "settings.app.form.enableGPUAcceleration": "Ota Gpu-kiihdytys käyttöön", | 212 | "settings.app.form.enableGPUAcceleration": "Ota Gpu-kiihdytys käyttöön", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Ota Salasanan lukitus käyttöön", | 214 | "settings.app.form.enableLock": "Ota Salasanan lukitus käyttöön", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Ota oikoluku käyttöön", | 217 | "settings.app.form.enableSpellchecking": "Ota oikoluku käyttöön", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Ota ilmoitukset käyttöön", | 312 | "settings.service.form.enableNotification": "Ota ilmoitukset käyttöön", |
311 | "settings.service.form.enableService": "Ota palvelu käyttöön", | 313 | "settings.service.form.enableService": "Ota palvelu käyttöön", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Lukemattomat viestit", | 315 | "settings.service.form.headlineBadges": "Lukemattomat viestit", |
313 | "settings.service.form.headlineDarkReaderSettings": "Tumman tilan asetukset", | 316 | "settings.service.form.headlineDarkReaderSettings": "Tumman tilan asetukset", |
314 | "settings.service.form.headlineGeneral": "Yleinen", | 317 | "settings.service.form.headlineGeneral": "Yleinen", |
diff --git a/src/i18n/locales/fr.json b/src/i18n/locales/fr.json index 80b32f495..7e2c591e2 100644 --- a/src/i18n/locales/fr.json +++ b/src/i18n/locales/fr.json | |||
@@ -53,7 +53,7 @@ | |||
53 | "infobar.requiredRequestsFailed": "Impossible d'accéder aux services et informations de l'utilisateur", | 53 | "infobar.requiredRequestsFailed": "Impossible d'accéder aux services et informations de l'utilisateur", |
54 | "infobar.servicesUpdated": "Vos services ont été mis à jour.", | 54 | "infobar.servicesUpdated": "Vos services ont été mis à jour.", |
55 | "infobar.updateAvailable": "Une nouvelle mise à jour de Ferdi est disponible.", | 55 | "infobar.updateAvailable": "Une nouvelle mise à jour de Ferdi est disponible.", |
56 | "infobox.dismiss": "Dismiss", | 56 | "infobox.dismiss": "Abandonner", |
57 | "invite.email.label": "Adresse Email", | 57 | "invite.email.label": "Adresse Email", |
58 | "invite.headline.friends": "Invitez 3 amis ou collègues", | 58 | "invite.headline.friends": "Invitez 3 amis ou collègues", |
59 | "invite.name.label": "Nom", | 59 | "invite.name.label": "Nom", |
@@ -74,7 +74,7 @@ | |||
74 | "login.email.label": "Adresse Email", | 74 | "login.email.label": "Adresse Email", |
75 | "login.headline": "S'identifier", | 75 | "login.headline": "S'identifier", |
76 | "login.invalidCredentials": "Email ou mot de passe invalide", | 76 | "login.invalidCredentials": "Email ou mot de passe invalide", |
77 | "login.link.password": "Reset password", | 77 | "login.link.password": "Réinitialiser le mot de passe", |
78 | "login.link.signup": "Créer un compte gratuit", | 78 | "login.link.signup": "Créer un compte gratuit", |
79 | "login.password.label": "Mot de passe", | 79 | "login.password.label": "Mot de passe", |
80 | "login.serverLogout": "Votre session a expiré. Reconnectez-vous s'il vous plaît.", | 80 | "login.serverLogout": "Votre session a expiré. Reconnectez-vous s'il vous plaît.", |
@@ -86,40 +86,40 @@ | |||
86 | "menu.app.autohideMenuBar": "Cacher automatiquement la barre de menu", | 86 | "menu.app.autohideMenuBar": "Cacher automatiquement la barre de menu", |
87 | "menu.app.checkForUpdates": "Vérifier les mises à jour", | 87 | "menu.app.checkForUpdates": "Vérifier les mises à jour", |
88 | "menu.app.hide": "Masquer", | 88 | "menu.app.hide": "Masquer", |
89 | "menu.app.hideOthers": "Hide Others", | 89 | "menu.app.hideOthers": "Masquer les autres", |
90 | "menu.app.unhide": "Unhide", | 90 | "menu.app.unhide": "Afficher l’élément masqué", |
91 | "menu.edit": "Éditer", | 91 | "menu.edit": "Éditer", |
92 | "menu.edit.copy": "Copy", | 92 | "menu.edit.copy": "Copier", |
93 | "menu.edit.cut": "Cut", | 93 | "menu.edit.cut": "Couper", |
94 | "menu.edit.delete": "Supprimer", | 94 | "menu.edit.delete": "Supprimer", |
95 | "menu.edit.emojiSymbols": "Emoji & Symboles", | 95 | "menu.edit.emojiSymbols": "Emoji & Symboles", |
96 | "menu.edit.findInPage": "Rechercher dans la page", | 96 | "menu.edit.findInPage": "Rechercher dans la page", |
97 | "menu.edit.paste": "Paste", | 97 | "menu.edit.paste": "Coller", |
98 | "menu.edit.pasteAndMatchStyle": "Paste And Match Style", | 98 | "menu.edit.pasteAndMatchStyle": "Coller en conservant la mise en forme", |
99 | "menu.edit.redo": "Redo", | 99 | "menu.edit.redo": "Rétablir", |
100 | "menu.edit.selectAll": "Select All", | 100 | "menu.edit.selectAll": "Sélectionner tout", |
101 | "menu.edit.speech": "Synthèse vocale", | 101 | "menu.edit.speech": "Synthèse vocale", |
102 | "menu.edit.startDictation": "Démarrer la dictée", | 102 | "menu.edit.startDictation": "Démarrer la dictée", |
103 | "menu.edit.startSpeaking": "Démarrer la synthèse vocale", | 103 | "menu.edit.startSpeaking": "Démarrer la synthèse vocale", |
104 | "menu.edit.stopSpeaking": "Arrêter la synthèse vocale", | 104 | "menu.edit.stopSpeaking": "Arrêter la synthèse vocale", |
105 | "menu.edit.undo": "Undo", | 105 | "menu.edit.undo": "Annuler", |
106 | "menu.file": "Fichier", | 106 | "menu.file": "Fichier", |
107 | "menu.help": "Help", | 107 | "menu.help": "Aide", |
108 | "menu.help.changelog": "Liste des modifications", | 108 | "menu.help.changelog": "Liste des modifications", |
109 | "menu.help.debugInfo": "Copier les informations de Debug", | 109 | "menu.help.debugInfo": "Copier les informations de Debug", |
110 | "menu.help.debugInfoCopiedBody": "Les informations de Debug ont été copié dans votre presse-papier.", | 110 | "menu.help.debugInfoCopiedBody": "Les informations de Debug ont été copié dans votre presse-papier.", |
111 | "menu.help.debugInfoCopiedHeadline": "Information de Debug de Ferdi", | 111 | "menu.help.debugInfoCopiedHeadline": "Information de Debug de Ferdi", |
112 | "menu.help.importExportData": "Import/Export Configuration Data", | 112 | "menu.help.importExportData": "Importer/Exporter les données de configuration", |
113 | "menu.help.learnMore": "En savoir plus", | 113 | "menu.help.learnMore": "En savoir plus", |
114 | "menu.help.privacy": "Déclaration de confidentialité", | 114 | "menu.help.privacy": "Déclaration de confidentialité", |
115 | "menu.help.publishDebugInfo": "Publier les informations de débogage", | 115 | "menu.help.publishDebugInfo": "Publier les informations de débogage", |
116 | "menu.help.support": "Assistance", | 116 | "menu.help.support": "Assistance", |
117 | "menu.help.tos": "Conditions d'utilisation", | 117 | "menu.help.tos": "Conditions d'utilisation", |
118 | "menu.services": "Services", | 118 | "menu.services": "Services", |
119 | "menu.services.activatePreviousService": "Activate previous service", | 119 | "menu.services.activatePreviousService": "Activer le service précédent", |
120 | "menu.services.addNewService": "Add New Service...", | 120 | "menu.services.addNewService": "Ajouter un nouveau service...", |
121 | "menu.services.goHome": "Accueil", | 121 | "menu.services.goHome": "Accueil", |
122 | "menu.services.setNextServiceActive": "Activate next service", | 122 | "menu.services.setNextServiceActive": "Activer le service suivant", |
123 | "menu.todos": "Todos", | 123 | "menu.todos": "Todos", |
124 | "menu.todos.enableTodos": "Activer Todos", | 124 | "menu.todos.enableTodos": "Activer Todos", |
125 | "menu.view": "Aperçu", | 125 | "menu.view": "Aperçu", |
@@ -127,7 +127,7 @@ | |||
127 | "menu.view.forward": "Avancer", | 127 | "menu.view.forward": "Avancer", |
128 | "menu.view.lockFerdi": "Verrouiller Ferdi", | 128 | "menu.view.lockFerdi": "Verrouiller Ferdi", |
129 | "menu.view.openQuickSwitch": "Ouvrir le changement rapide", | 129 | "menu.view.openQuickSwitch": "Ouvrir le changement rapide", |
130 | "menu.view.reloadFerdi": "Reload Ferdi", | 130 | "menu.view.reloadFerdi": "Recharger Ferdi", |
131 | "menu.view.reloadService": "Redémarrer le service", | 131 | "menu.view.reloadService": "Redémarrer le service", |
132 | "menu.view.reloadTodos": "Recharger les Todos", | 132 | "menu.view.reloadTodos": "Recharger les Todos", |
133 | "menu.view.resetZoom": "Taille actuelle", | 133 | "menu.view.resetZoom": "Taille actuelle", |
@@ -147,11 +147,11 @@ | |||
147 | "menu.workspaces.defaultWorkspace": "Tous les services", | 147 | "menu.workspaces.defaultWorkspace": "Tous les services", |
148 | "menu.workspaces.openWorkspaceDrawer": "Ouvrir l'espace de travail", | 148 | "menu.workspaces.openWorkspaceDrawer": "Ouvrir l'espace de travail", |
149 | "password.email.label": "Adresse Email", | 149 | "password.email.label": "Adresse Email", |
150 | "password.headline": "Reset password", | 150 | "password.headline": "Réinitialiser le mot de passe", |
151 | "password.link.login": "Connectez-vous à votre compte", | 151 | "password.link.login": "Connectez-vous à votre compte", |
152 | "password.link.signup": "Créer un compte gratuit", | 152 | "password.link.signup": "Créer un compte gratuit", |
153 | "password.noUser": "Aucun utilisateur n'a été trouvé avec cette adresse email", | 153 | "password.noUser": "Aucun utilisateur n'a été trouvé avec cette adresse de courriel", |
154 | "password.successInfo": "Your new password was sent to your email address", | 154 | "password.successInfo": "Votre nouveau mot de passe a été envoyé à votre adresse de courriel", |
155 | "service.crashHandler.action": "Recharger {name}", | 155 | "service.crashHandler.action": "Recharger {name}", |
156 | "service.crashHandler.autoReload": "Tentative de restauration automatique de {name} dans {seconds} secondes", | 156 | "service.crashHandler.autoReload": "Tentative de restauration automatique de {name} dans {seconds} secondes", |
157 | "service.crashHandler.headline": "Oh non!", | 157 | "service.crashHandler.headline": "Oh non!", |
@@ -166,7 +166,7 @@ | |||
166 | "service.webviewLoader.loading": "Chargement de {service}", | 166 | "service.webviewLoader.loading": "Chargement de {service}", |
167 | "services.getStarted": "Commencer", | 167 | "services.getStarted": "Commencer", |
168 | "services.login": "Veuillez vous connecter pour utiliser Ferdi.", | 168 | "services.login": "Veuillez vous connecter pour utiliser Ferdi.", |
169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", | 169 | "services.serverInfo": "Optionnellement, vous pouvez changer de serveur Ferdi en cliquant sur le contrôle dans le coin inférieur gauche. Si vous basculez d'un serveur hébergé à une utilisation de Ferdi sans compte, vous pouvez exporter vos données depuis ce serveur et ensuite l'importer en utilisant le menu Aide pour ressusciter tous vos espaces de travail et services configurés !", |
170 | "services.serverless": "Utiliser Ferdi sans compte", | 170 | "services.serverless": "Utiliser Ferdi sans compte", |
171 | "services.welcome": "Bienvenue dans Ferdi", | 171 | "services.welcome": "Bienvenue dans Ferdi", |
172 | "settings.account.account.editButton": "Modifier le compte", | 172 | "settings.account.account.editButton": "Modifier le compte", |
@@ -187,11 +187,11 @@ | |||
187 | "settings.account.tryReloadUserInfoRequest": "Réessayer", | 187 | "settings.account.tryReloadUserInfoRequest": "Réessayer", |
188 | "settings.account.userInfoRequestFailed": "Impossible de charger les informations de l'utilisateur", | 188 | "settings.account.userInfoRequestFailed": "Impossible de charger les informations de l'utilisateur", |
189 | "settings.account.yourLicense": "Votre licence Ferdi :", | 189 | "settings.account.yourLicense": "Votre licence Ferdi :", |
190 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: {defaultAccentColor})", | 190 | "settings.app.accentColorInfo": "Écrivez votre couleur d'accentuation dans un format compatible avec CSS. (Par défaut: {defaultAccentColor})", |
191 | "settings.app.buttonClearAllCache": "Vider le cache", | 191 | "settings.app.buttonClearAllCache": "Vider le cache", |
192 | "settings.app.buttonInstallUpdate": "Redémarrer et installer la mise à jour", | 192 | "settings.app.buttonInstallUpdate": "Redémarrer et installer la mise à jour", |
193 | "settings.app.buttonOpenFerdiProfileFolder": "Afficher le dossier du profil", | 193 | "settings.app.buttonOpenFerdiProfileFolder": "Afficher le dossier du profil", |
194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Open Service Recipes folder", | 194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Ouvrir le dossier des recettes de service (Recipes)", |
195 | "settings.app.buttonSearchForUpdate": "Vérifier les mises à jour", | 195 | "settings.app.buttonSearchForUpdate": "Vérifier les mises à jour", |
196 | "settings.app.cacheInfo": "Le cache de Ferdi occupe actuellement {size} en espace disque.", | 196 | "settings.app.cacheInfo": "Le cache de Ferdi occupe actuellement {size} en espace disque.", |
197 | "settings.app.cacheNotCleared": "Impossible de vider toute la cache", | 197 | "settings.app.cacheNotCleared": "Impossible de vider toute la cache", |
@@ -204,16 +204,18 @@ | |||
204 | "settings.app.form.autoLaunchOnStart": "Lancer Ferdi au démarrage", | 204 | "settings.app.form.autoLaunchOnStart": "Lancer Ferdi au démarrage", |
205 | "settings.app.form.automaticUpdates": "Activer les mises à jour", | 205 | "settings.app.form.automaticUpdates": "Activer les mises à jour", |
206 | "settings.app.form.beta": "Accepter les versions bêta", | 206 | "settings.app.form.beta": "Accepter les versions bêta", |
207 | "settings.app.form.clipboardNotifications": "Don't show notifications for clipboard events", | 207 | "settings.app.form.clipboardNotifications": "Ne pas afficher les notifications pour les événements du presse-papiers", |
208 | "settings.app.form.closeToSystemTray": "Afficher Ferdi dans la barre d'état système", | 208 | "settings.app.form.closeToSystemTray": "Lors de la fermeture, laisser Ferdi actif dans la barre d'état système", |
209 | "settings.app.form.confirmOnQuit": "Confirm when quitting Ferdi", | 209 | "settings.app.form.confirmOnQuit": "Confirmer lorsque vous quittez Ferdi", |
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Serveur Todo personnalisé", |
211 | "settings.app.form.darkMode": "Activer le mode sombre", | 211 | "settings.app.form.darkMode": "Activer le mode sombre", |
212 | "settings.app.form.enableGPUAcceleration": "Activer l'accélération GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Activer l'accélération GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Activer le raccourci global pour masquer Ferdi", | ||
213 | "settings.app.form.enableLock": "Activer le verrouillage par mot de passe", | 214 | "settings.app.form.enableLock": "Activer le verrouillage par mot de passe", |
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 215 | "settings.app.form.enableLongPressServiceHint": "Activer le service lors d'un appui long sur son raccourci", |
216 | "settings.app.form.enableMenuBar": "Toujours afficher Ferdi dans la barre de menu", | ||
215 | "settings.app.form.enableSpellchecking": "Activer la vérification orthographique", | 217 | "settings.app.form.enableSpellchecking": "Activer la vérification orthographique", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Toujours afficher l'icône Ferdi dans la zone de notification", |
217 | "settings.app.form.enableTodos": "Activer Ferdi Todos", | 219 | "settings.app.form.enableTodos": "Activer Ferdi Todos", |
218 | "settings.app.form.hibernateOnStartup": "Garder les services en veille prolongée au démarrage", | 220 | "settings.app.form.hibernateOnStartup": "Garder les services en veille prolongée au démarrage", |
219 | "settings.app.form.hibernationStrategy": "Stratégie d'hibernation", | 221 | "settings.app.form.hibernationStrategy": "Stratégie d'hibernation", |
@@ -225,7 +227,7 @@ | |||
225 | "settings.app.form.minimizeToSystemTray": "Minimiser Ferdi dans la zone de notification", | 227 | "settings.app.form.minimizeToSystemTray": "Minimiser Ferdi dans la zone de notification", |
226 | "settings.app.form.navigationBarBehaviour": "Comportement de la barre de navigation", | 228 | "settings.app.form.navigationBarBehaviour": "Comportement de la barre de navigation", |
227 | "settings.app.form.notifyTaskBarOnMessage": "Notification sur la barre des tâches/le dock lors d'un nouveau message", | 229 | "settings.app.form.notifyTaskBarOnMessage": "Notification sur la barre des tâches/le dock lors d'un nouveau message", |
228 | "settings.app.form.passwordToggle": "Password toggle", | 230 | "settings.app.form.passwordToggle": "Afficher le mot de passe", |
229 | "settings.app.form.predefinedTodoServer": "Serveur Todo", | 231 | "settings.app.form.predefinedTodoServer": "Serveur Todo", |
230 | "settings.app.form.privateNotifications": "Ne pas afficher le contenu des notifications", | 232 | "settings.app.form.privateNotifications": "Ne pas afficher le contenu des notifications", |
231 | "settings.app.form.reloadAfterResume": "Recharger Ferdi après la reprise du système", | 233 | "settings.app.form.reloadAfterResume": "Recharger Ferdi après la reprise du système", |
@@ -233,38 +235,38 @@ | |||
233 | "settings.app.form.scheduledDNDEnabled": "Activer Ne-pas-Déranger", | 235 | "settings.app.form.scheduledDNDEnabled": "Activer Ne-pas-Déranger", |
234 | "settings.app.form.scheduledDNDEnd": "À", | 236 | "settings.app.form.scheduledDNDEnd": "À", |
235 | "settings.app.form.scheduledDNDStart": "De", | 237 | "settings.app.form.scheduledDNDStart": "De", |
236 | "settings.app.form.searchEngine": "Search engine", | 238 | "settings.app.form.searchEngine": "Moteur de recherche", |
237 | "settings.app.form.sentry": "Envoyer des données de télémétrie", | 239 | "settings.app.form.sentry": "Envoyer des données de télémétrie", |
238 | "settings.app.form.serviceRibbonWidth": "Largeur du menu", | 240 | "settings.app.form.serviceRibbonWidth": "Largeur du menu", |
239 | "settings.app.form.showDisabledServices": "Afficher les onglets des services désactivés", | 241 | "settings.app.form.showDisabledServices": "Afficher les onglets des services désactivés", |
240 | "settings.app.form.showDragArea": "Afficher les zones de glisser-déposer dans la fenêtre", | 242 | "settings.app.form.showDragArea": "Afficher les zones de glisser-déposer dans la fenêtre", |
241 | "settings.app.form.showMessagesBadgesWhenMuted": "Afficher les badges de messages non lus quand les notifications sont désactivées", | 243 | "settings.app.form.showMessagesBadgesWhenMuted": "Afficher les badges de messages non lus quand les notifications sont désactivées", |
242 | "settings.app.form.splitMode": "Enable Split View Mode", | 244 | "settings.app.form.splitMode": "Activer le Mode Vue Divisée", |
243 | "settings.app.form.startMinimized": "Démarrage minimisé", | 245 | "settings.app.form.startMinimized": "Démarrage minimisé", |
244 | "settings.app.form.universalDarkMode": "Activer le mode sombre universel", | 246 | "settings.app.form.universalDarkMode": "Activer le mode sombre universel", |
245 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", | 247 | "settings.app.form.useTouchIdToUnlock": "Autoriser l'utilisation du système de lecture d'empreinte digitale pour déverrouiller Ferdi", |
246 | "settings.app.form.useVerticalStyle": "Use horizontal style", | 248 | "settings.app.form.useVerticalStyle": "Utiliser un style horizontal", |
247 | "settings.app.form.wakeUpStrategy": "Wake up strategy", | 249 | "settings.app.form.wakeUpStrategy": "Stratégie de réveil", |
248 | "settings.app.headlineAdvanced": "Paramètres avancés", | 250 | "settings.app.headlineAdvanced": "Paramètres avancés", |
249 | "settings.app.headlineAppearance": "Apparence", | 251 | "settings.app.headlineAppearance": "Apparence", |
250 | "settings.app.headlineGeneral": "Général", | 252 | "settings.app.headlineGeneral": "Général", |
251 | "settings.app.headlineLanguage": "Langue", | 253 | "settings.app.headlineLanguage": "Langue", |
252 | "settings.app.headlinePrivacy": "Privacy", | 254 | "settings.app.headlinePrivacy": "Confidentialité", |
253 | "settings.app.headlineUpdates": "Mises à jour", | 255 | "settings.app.headlineUpdates": "Mises à jour", |
254 | "settings.app.hibernateInfo": "Par défaut, Ferdi gardera tous vos services ouverts et chargés en arrière-plan afin qu'ils soient prêts lorsque vous voulez les utiliser. Le service d'hibernation déchargera vos services après un montant spécifié. Ceci est utile pour sauver de la RAM ou garder les services de ralentir votre ordinateur.", | 256 | "settings.app.hibernateInfo": "Par défaut, Ferdi gardera tous vos services ouverts et chargés en arrière-plan afin qu'ils soient prêts lorsque vous voulez les utiliser. Le service d'hibernation déchargera vos services après un montant spécifié. Ceci est utile pour sauver de la RAM ou garder les services de ralentir votre ordinateur.", |
255 | "settings.app.inactivityLockInfo": "Minutes d'inactivité, après lesquelles Ferdi devrait se verrouiller automatiquement. Utilisez 0 pour désactiver", | 257 | "settings.app.inactivityLockInfo": "Minutes d'inactivité, après lesquelles Ferdi devrait se verrouiller automatiquement. Utilisez 0 pour désactiver", |
256 | "settings.app.languageDisclaimer": "Les traductions officielles sont l'anglais et l'allemand. Toutes les autres langues sont des traductions faites par la communauté.", | 258 | "settings.app.languageDisclaimer": "Les traductions officielles sont l'anglais et l'allemand. Toutes les autres langues sont des traductions faites par la communauté.", |
257 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", | 259 | "settings.app.lockInfo": "Le verrouillage par mot de passe vous permet de garder vos messages protégés.\nPour l'utilisation du verrouillage par mot de passe, vous serez invité à entrer votre mot de passe chaque fois que vous démarrez Ferdi ou que vous verrouillerez Ferdi vous-même en utilisant le symbole de verrouillage en bas à gauche ou le raccourci {lockShortcut}.", |
258 | "settings.app.lockedPassword": "Mot de passe", | 260 | "settings.app.lockedPassword": "Mot de passe", |
259 | "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdi.", | 261 | "settings.app.lockedPasswordInfo": "Veuillez vous assurer de définir un mot de passe dont vous vous souviendrez.\nSi vous perdez ce mot de passe, vous devrez réinstaller Ferdi.", |
260 | "settings.app.restartRequired": "Les modifications nécessitent un redémarrage", | 262 | "settings.app.restartRequired": "Les modifications nécessitent un redémarrage", |
261 | "settings.app.scheduledDNDInfo": "Planifier le Ne-pas-Déranger vous permet de définir une période de temps dans lequel vous ne voulez pas de notifications de Ferdi.", | 263 | "settings.app.scheduledDNDInfo": "Planifier le Ne-pas-Déranger vous permet de définir une période de temps dans lequel vous ne voulez pas de notifications de Ferdi.", |
262 | "settings.app.scheduledDNDTimeInfo": "Le temps est en format 24 heures. La fin du temps peut être avant le début du temps (ex: début 17:00, fin 09:00) pour activer le Ne-pas-Déranger durant la nuit.", | 264 | "settings.app.scheduledDNDTimeInfo": "Le temps est en format 24 heures. La fin du temps peut être avant le début du temps (ex: début 17:00, fin 09:00) pour activer le Ne-pas-Déranger durant la nuit.", |
263 | "settings.app.sentryInfo": "Sending telemetry data allows us to find errors in Ferdi - we will not send any personal information like your message data!", | 265 | "settings.app.sentryInfo": "L'envoi de données de télémétrie nous permet de trouver des erreurs dans Ferdi - nous n'enverrons aucune information personnelle comme vos données de message!", |
264 | "settings.app.spellCheckerLanguageInfo": "Ferdi utilise le correcteur orthographique intégré de votre Mac pour vérifier les fautes de frappe. Si vous voulez changer les langues pour lesquelles le correcteur vérifie l'orthographe, vous pouvez le faire dans les préférences système de votre Mac.", | 266 | "settings.app.spellCheckerLanguageInfo": "Ferdi utilise le correcteur orthographique intégré de votre Mac pour vérifier les fautes de frappe. Si vous voulez changer les langues pour lesquelles le correcteur vérifie l'orthographe, vous pouvez le faire dans les préférences système de votre Mac.", |
265 | "settings.app.subheadlineCache": "Cache", | 267 | "settings.app.subheadlineCache": "Cache", |
266 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", | 268 | "settings.app.subheadlineFerdiProfile": "Profil Ferdi", |
267 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature.", | 269 | "settings.app.todoServerInfo": "Ce serveur sera utilisé pour la fonctionnalité \"Ferdi Todo\".", |
268 | "settings.app.translationHelp": "Aidez-nous à traduire Ferdi dans votre langue.", | 270 | "settings.app.translationHelp": "Aidez-nous à traduire Ferdi dans votre langue.", |
269 | "settings.app.universalDarkModeInfo": "Le mode sombre universel tente de générer dynamiquement des styles de mode sombre pour les services qui ne sont pas encore supportés.", | 271 | "settings.app.universalDarkModeInfo": "Le mode sombre universel tente de générer dynamiquement des styles de mode sombre pour les services qui ne sont pas encore supportés.", |
270 | "settings.app.updateStatusAvailable": "Mise à jour disponible, téléchargement en cours...", | 272 | "settings.app.updateStatusAvailable": "Mise à jour disponible, téléchargement en cours...", |
@@ -283,12 +285,12 @@ | |||
283 | "settings.recipes.customService.headline.communityRecipes": "Recettes tiers communautaire", | 285 | "settings.recipes.customService.headline.communityRecipes": "Recettes tiers communautaire", |
284 | "settings.recipes.customService.headline.customRecipes": "Recettes tiers modifiées", | 286 | "settings.recipes.customService.headline.customRecipes": "Recettes tiers modifiées", |
285 | "settings.recipes.customService.headline.devRecipes": "Vos recettes de service de développement", | 287 | "settings.recipes.customService.headline.devRecipes": "Vos recettes de service de développement", |
286 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", | 288 | "settings.recipes.customService.intro": "Pour ajouter un service personnalisé, copiez la recette du service à :", |
287 | "settings.recipes.customService.openDevDocs": "Documentation de Développeur", | 289 | "settings.recipes.customService.openDevDocs": "Documentation de Développeur", |
288 | "settings.recipes.customService.openFolder": "Open folder", | 290 | "settings.recipes.customService.openFolder": "Ouvrir le dossier", |
289 | "settings.recipes.headline": "Services disponibles", | 291 | "settings.recipes.headline": "Services disponibles", |
290 | "settings.recipes.missingService": "Un service est manquant?", | 292 | "settings.recipes.missingService": "Un service est manquant?", |
291 | "settings.recipes.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 293 | "settings.recipes.nothingFound": "Désolé, mais aucun service ne correspond à votre recherche - mais vous pouvez toujours l'ajouter en utilisant l'option \"Custom Website\". Veuillez noter que le site Web pourrait afficher plus de services qui ont été ajoutés à Ferdi depuis la version sur laquelle vous êtes actuellement. Pour obtenir ces nouveaux services, veuillez envisager la mise à niveau vers une version plus récente de Ferdi.", |
292 | "settings.recipes.servicesSuccessfulAddedInfo": "Le service a été ajouté avec succès", | 294 | "settings.recipes.servicesSuccessfulAddedInfo": "Le service a été ajouté avec succès", |
293 | "settings.searchService": "Chercher un service", | 295 | "settings.searchService": "Chercher un service", |
294 | "settings.service.error.goBack": "Retour aux services", | 296 | "settings.service.error.goBack": "Retour aux services", |
@@ -301,14 +303,15 @@ | |||
301 | "settings.service.form.darkReaderBrightness": "Luminosité de Dark Reader", | 303 | "settings.service.form.darkReaderBrightness": "Luminosité de Dark Reader", |
302 | "settings.service.form.darkReaderContrast": "Contraste de Dark Reader", | 304 | "settings.service.form.darkReaderContrast": "Contraste de Dark Reader", |
303 | "settings.service.form.darkReaderSepia": "Sepia de Dark Reader", | 305 | "settings.service.form.darkReaderSepia": "Sepia de Dark Reader", |
304 | "settings.service.form.deleteButton": "Delete service", | 306 | "settings.service.form.deleteButton": "Supprimer le service", |
305 | "settings.service.form.editServiceHeadline": "Modifier {name}", | 307 | "settings.service.form.editServiceHeadline": "Modifier {name}", |
306 | "settings.service.form.enableAudio": "Activer l'audio", | 308 | "settings.service.form.enableAudio": "Activer l'audio", |
307 | "settings.service.form.enableBadge": "Afficher le badge des messages non lus", | 309 | "settings.service.form.enableBadge": "Afficher le badge des messages non lus", |
308 | "settings.service.form.enableDarkMode": "Activer le mode sombre", | 310 | "settings.service.form.enableDarkMode": "Activer le mode sombre", |
309 | "settings.service.form.enableHibernation": "Activer l'hibernation", | 311 | "settings.service.form.enableHibernation": "Activer la veille prolongée", |
310 | "settings.service.form.enableNotification": "Activer les notifications", | 312 | "settings.service.form.enableNotification": "Activer les notifications", |
311 | "settings.service.form.enableService": "Activer le service", | 313 | "settings.service.form.enableService": "Activer le service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Badge des messages non lus", | 315 | "settings.service.form.headlineBadges": "Badge des messages non lus", |
313 | "settings.service.form.headlineDarkReaderSettings": "Paramètre de Dark Reader", | 316 | "settings.service.form.headlineDarkReaderSettings": "Paramètre de Dark Reader", |
314 | "settings.service.form.headlineGeneral": "Général", | 317 | "settings.service.form.headlineGeneral": "Général", |
@@ -321,18 +324,18 @@ | |||
321 | "settings.service.form.isHibernatedEnabledInfo": "Quand activé, les services seront éteints après un moment pour économiser les ressources systèmes.", | 324 | "settings.service.form.isHibernatedEnabledInfo": "Quand activé, les services seront éteints après un moment pour économiser les ressources systèmes.", |
322 | "settings.service.form.isMutedInfo": "Lorsque désactivé, tous les sons de notifications ainsi que l'audio sont coupés", | 325 | "settings.service.form.isMutedInfo": "Lorsque désactivé, tous les sons de notifications ainsi que l'audio sont coupés", |
323 | "settings.service.form.name": "Nom", | 326 | "settings.service.form.name": "Nom", |
324 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", | 327 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Afficher uniquement les Favoris dans le nombre de non-lus", |
325 | "settings.service.form.openDarkmodeCss": "Ouvrir darkmode.css", | 328 | "settings.service.form.openDarkmodeCss": "Ouvrir darkmode.css", |
326 | "settings.service.form.openUserCss": "Ouvrir user.css", | 329 | "settings.service.form.openUserCss": "Ouvrir user.css", |
327 | "settings.service.form.openUserJs": "Ouvrir user.js", | 330 | "settings.service.form.openUserJs": "Ouvrir user.js", |
328 | "settings.service.form.proxy.headline": "Paramètres proxy HTTP/HTTPS", | 331 | "settings.service.form.proxy.headline": "Paramètres proxy HTTP/HTTPS", |
329 | "settings.service.form.proxy.host": "Hôte/IP du proxy", | 332 | "settings.service.form.proxy.host": "Hôte/IP du proxy", |
330 | "settings.service.form.proxy.info": "Proxy settings will not be synchronized with the Ferdi servers.", | 333 | "settings.service.form.proxy.info": "Les paramètres du proxy ne seront pas synchronisés avec les serveurs Ferdi.", |
331 | "settings.service.form.proxy.isEnabled": "Utiliser un proxy", | 334 | "settings.service.form.proxy.isEnabled": "Utiliser un proxy", |
332 | "settings.service.form.proxy.password": "Password (optional)", | 335 | "settings.service.form.proxy.password": "Mot de passe (facultatif)", |
333 | "settings.service.form.proxy.port": "Port", | 336 | "settings.service.form.proxy.port": "Port", |
334 | "settings.service.form.proxy.restartInfo": "Veuillez redémarrer Ferdi après avoir modifié les paramètres proxy.", | 337 | "settings.service.form.proxy.restartInfo": "Veuillez redémarrer Ferdi après avoir modifié les paramètres proxy.", |
335 | "settings.service.form.proxy.user": "User (optional)", | 338 | "settings.service.form.proxy.user": "Utilisateur (facultatif)", |
336 | "settings.service.form.recipeFileInfo": "Vos fichiers utilisateur seront insérés dans la page Web afin que vous puissiez personnaliser les services comme vous le souhaitez. Les fichiers utilisateurs sont stockés localement et ne sont pas transférés à d'autres ordinateurs en utilisant le même compte.", | 339 | "settings.service.form.recipeFileInfo": "Vos fichiers utilisateur seront insérés dans la page Web afin que vous puissiez personnaliser les services comme vous le souhaitez. Les fichiers utilisateurs sont stockés localement et ne sont pas transférés à d'autres ordinateurs en utilisant le même compte.", |
337 | "settings.service.form.saveButton": "Enregistrer le service", | 340 | "settings.service.form.saveButton": "Enregistrer le service", |
338 | "settings.service.form.tabHosted": "Hébergé", | 341 | "settings.service.form.tabHosted": "Hébergé", |
@@ -344,7 +347,7 @@ | |||
344 | "settings.services.discoverServices": "Découvrir les services", | 347 | "settings.services.discoverServices": "Découvrir les services", |
345 | "settings.services.headline": "Vos services", | 348 | "settings.services.headline": "Vos services", |
346 | "settings.services.noServicesAdded": "Commencez par ajouter un service.", | 349 | "settings.services.noServicesAdded": "Commencez par ajouter un service.", |
347 | "settings.services.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 350 | "settings.services.nothingFound": "Désolé, mais aucun service ne correspond à votre recherche - mais vous pouvez toujours l'ajouter en utilisant l'option \"Custom Website\". Veuillez noter que le site Web pourrait afficher plus de services qui ont été ajoutés à Ferdi depuis la version sur laquelle vous êtes actuellement. Pour obtenir ces nouveaux services, veuillez envisager la mise à niveau vers une version plus récente de Ferdi.", |
348 | "settings.services.servicesRequestFailed": "Impossible de charger vos services", | 351 | "settings.services.servicesRequestFailed": "Impossible de charger vos services", |
349 | "settings.services.tooltip.isDisabled": "Ce service est désactivé", | 352 | "settings.services.tooltip.isDisabled": "Ce service est désactivé", |
350 | "settings.services.tooltip.isMuted": "Tous les sons sont coupés", | 353 | "settings.services.tooltip.isMuted": "Tous les sons sont coupés", |
@@ -353,22 +356,22 @@ | |||
353 | "settings.supportFerdi.aboutIntro": "<p>Ferdi est une application open-source et dirigée par la communauté.</p><p>Merci à toutes les personnes qui rendent cela possible :</p>", | 356 | "settings.supportFerdi.aboutIntro": "<p>Ferdi est une application open-source et dirigée par la communauté.</p><p>Merci à toutes les personnes qui rendent cela possible :</p>", |
354 | "settings.supportFerdi.bannerText": "Voulez-vous nous aider à améliorer Ferdi ?", | 357 | "settings.supportFerdi.bannerText": "Voulez-vous nous aider à améliorer Ferdi ?", |
355 | "settings.supportFerdi.headline": "À propos de Ferdi", | 358 | "settings.supportFerdi.headline": "À propos de Ferdi", |
356 | "settings.supportFerdi.openSurvey": "Open survey", | 359 | "settings.supportFerdi.openSurvey": "Ouvrir l'enquête", |
357 | "settings.supportFerdi.textDonation": "Si vous avez envie de soutenir le développement de Ferdi par un don, vous pouvez le faire via", | 360 | "settings.supportFerdi.textDonation": "Si vous avez envie de soutenir le développement de Ferdi par un don, vous pouvez le faire via", |
358 | "settings.supportFerdi.textDonationAnd": "et", | 361 | "settings.supportFerdi.textDonationAnd": "et", |
359 | "settings.supportFerdi.textExpenses": "Bien que les bénévoles fassent la majeure partie du travail, nous avons besoin de payer pour les serveurs et les certificats. En tant que communauté, nous sommes totalement transparents sur les fonds que nous collectons et dépensons - voir nos", | 362 | "settings.supportFerdi.textExpenses": "Bien que les bénévoles fassent la majeure partie du travail, nous avons besoin de payer pour les serveurs et les certificats. En tant que communauté, nous sommes totalement transparents sur les fonds que nous collectons et dépensons - voir nos", |
360 | "settings.supportFerdi.textGitHubSponsors": "Sponsors sur GitHub", | 363 | "settings.supportFerdi.textGitHubSponsors": "Sponsors sur GitHub", |
361 | "settings.supportFerdi.textListContributors": "Full list of contributors", | 364 | "settings.supportFerdi.textListContributors": "Liste complète des contributeurs", |
362 | "settings.supportFerdi.textListContributorsHere": "ici", | 365 | "settings.supportFerdi.textListContributorsHere": "ici", |
363 | "settings.supportFerdi.textOpenCollective": "Open Collective", | 366 | "settings.supportFerdi.textOpenCollective": "Open Collective", |
364 | "settings.supportFerdi.textSupportWelcome": "Toute aide est toujours la bienvenue. Vous pouvez trouver une liste de l'aide dont nous avons besoin", | 367 | "settings.supportFerdi.textSupportWelcome": "Toute aide est toujours la bienvenue. Vous pouvez trouver une liste de l'aide dont nous avons besoin", |
365 | "settings.supportFerdi.textSupportWelcomeHere": "ici", | 368 | "settings.supportFerdi.textSupportWelcomeHere": "ici", |
366 | "settings.supportFerdi.textVolunteers": "Le développement de Ferdi est fait par des bénévoles. Des gens qui utilisent Ferdi comme vous maintiennent, réparent et améliorent Ferdi pendant leur temps libre.", | 369 | "settings.supportFerdi.textVolunteers": "Le développement de Ferdi est fait par des bénévoles. Des gens qui utilisent Ferdi comme vous. Ils maintiennent, réparent et améliorent Ferdi pendant leur temps libre.", |
367 | "settings.supportFerdi.title": "Vous aimez Ferdi ?", | 370 | "settings.supportFerdi.title": "Vous aimez Ferdi ?", |
368 | "settings.team.contentHeadline": "Gestion d'équipe Franz", | 371 | "settings.team.contentHeadline": "Gestion d'équipe Franz", |
369 | "settings.team.copy": "La gestion d'équipe de Franz vous permet de gérer les abonnements Franz pour plusieurs utilisateurs. N’oubliez pas que le fait d’avoir un abonnement Franz Premium ne vous donnera aucun avantage dans l'utilisation de Ferdi : la seule raison pour laquelle vous avez encore accès à la gestion d’équipe est que vous pouvez gérer vos équipes Franz héritées et que vous ne perdez donc aucune fonctionnalité dans la gestion de votre compte.", | 372 | "settings.team.copy": "La gestion d'équipe de Franz vous permet de gérer les abonnements Franz pour plusieurs utilisateurs. N’oubliez pas que le fait d’avoir un abonnement Franz Premium ne vous donnera aucun avantage dans l'utilisation de Ferdi : la seule raison pour laquelle vous avez encore accès à la gestion d’équipe est que vous pouvez gérer vos équipes Franz héritées et que vous ne perdez donc aucune fonctionnalité dans la gestion de votre compte.", |
370 | "settings.team.headline": "Équipe", | 373 | "settings.team.headline": "Équipe", |
371 | "settings.team.intro": "You are currently using Franz Servers, which is why you have access to Team Management.", | 374 | "settings.team.intro": "Vous utilisez actuellement les serveurs Franz, c'est pourquoi vous avez accès à la gestion d'équipe.", |
372 | "settings.team.manageAction": "Gérez votre équipe sur meetfranz.com", | 375 | "settings.team.manageAction": "Gérez votre équipe sur meetfranz.com", |
373 | "settings.team.teamsUnavailable": "Les équipes sont indisponibles", | 376 | "settings.team.teamsUnavailable": "Les équipes sont indisponibles", |
374 | "settings.team.teamsUnavailableInfo": "Les équipes sont actuellement disponibles uniquement lorsque vous utilisez le serveur de Franz et après avoir payé pour Franz Professionnel. Veuillez changer votre serveur à https://api.franzinfra.com pour utiliser des équipes.", | 377 | "settings.team.teamsUnavailableInfo": "Les équipes sont actuellement disponibles uniquement lorsque vous utilisez le serveur de Franz et après avoir payé pour Franz Professionnel. Veuillez changer votre serveur à https://api.franzinfra.com pour utiliser des équipes.", |
@@ -378,8 +381,8 @@ | |||
378 | "settings.user.form.accountType.non-profit": "Non-lucratif", | 381 | "settings.user.form.accountType.non-profit": "Non-lucratif", |
379 | "settings.user.form.currentPassword": "Mot de passe actuel", | 382 | "settings.user.form.currentPassword": "Mot de passe actuel", |
380 | "settings.user.form.email": "Email", | 383 | "settings.user.form.email": "Email", |
381 | "settings.user.form.firstname": "First Name", | 384 | "settings.user.form.firstname": "Prénom", |
382 | "settings.user.form.lastname": "Last Name", | 385 | "settings.user.form.lastname": "Nom", |
383 | "settings.user.form.newPassword": "Nouveau mot de passe", | 386 | "settings.user.form.newPassword": "Nouveau mot de passe", |
384 | "settings.workspace.add.form.name": "Nom", | 387 | "settings.workspace.add.form.name": "Nom", |
385 | "settings.workspace.add.form.submitButton": "Créer l'espace de travail", | 388 | "settings.workspace.add.form.submitButton": "Créer l'espace de travail", |
@@ -396,7 +399,7 @@ | |||
396 | "settings.workspaces.tryReloadWorkspaces": "Réessayer", | 399 | "settings.workspaces.tryReloadWorkspaces": "Réessayer", |
397 | "settings.workspaces.updatedInfo": "Vos modifications ont été enregistrées", | 400 | "settings.workspaces.updatedInfo": "Vos modifications ont été enregistrées", |
398 | "settings.workspaces.workspaceFeatureHeadline": "Présentation des Espaces de travail de Ferdi", | 401 | "settings.workspaces.workspaceFeatureHeadline": "Présentation des Espaces de travail de Ferdi", |
399 | "settings.workspaces.workspaceFeatureInfo": "Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.", | 402 | "settings.workspaces.workspaceFeatureInfo": "Les Espaces de travail de Ferdi vous permettant de rester concentré sur ce qui est important. Créez différents groupes de services et naviguez facilement entre eux à n'importe quel moment. Vous décidez de quels services vous avez besoin, où et quand, ainsi nous pouvons vous aider à rester concentré sur votre travail - ou à le quitter dès que vous le souhaitez.", |
400 | "settings.workspaces.workspacesRequestFailed": "Impossible de charger vos espaces de travail", | 403 | "settings.workspaces.workspacesRequestFailed": "Impossible de charger vos espaces de travail", |
401 | "setupAssistant.headline": "Commençons", | 404 | "setupAssistant.headline": "Commençons", |
402 | "setupAssistant.subheadline": "Choisissez parmi nos services les plus utilisés et revenez maintenant sur votre messagerie.", | 405 | "setupAssistant.subheadline": "Choisissez parmi nos services les plus utilisés et revenez maintenant sur votre messagerie.", |
@@ -411,33 +414,33 @@ | |||
411 | "sidebar.unmuteApp": "Activer les notifications et les sons", | 414 | "sidebar.unmuteApp": "Activer les notifications et les sons", |
412 | "signup.email.label": "Adresse Email", | 415 | "signup.email.label": "Adresse Email", |
413 | "signup.emailDuplicate": "Cette adresse email est déjà utilisée", | 416 | "signup.emailDuplicate": "Cette adresse email est déjà utilisée", |
414 | "signup.firstname.label": "First Name", | 417 | "signup.firstname.label": "Prénom", |
415 | "signup.headline": "S'inscrire", | 418 | "signup.headline": "S'inscrire", |
416 | "signup.lastname.label": "Last Name", | 419 | "signup.lastname.label": "Nom", |
417 | "signup.legal.info": "En créant un compte Ferdi, vous acceptez la", | 420 | "signup.legal.info": "En créant un compte Ferdi, vous acceptez la", |
418 | "signup.legal.privacy": "Déclaration de confidentialité", | 421 | "signup.legal.privacy": "Déclaration de confidentialité", |
419 | "signup.legal.terms": "Conditions d'utilisation", | 422 | "signup.legal.terms": "Conditions d'utilisation", |
420 | "signup.link.login": "Vous avez déjà un compte? Connectez-vous", | 423 | "signup.link.login": "Vous avez déjà un compte? Connectez-vous", |
421 | "signup.password.label": "Mot de passe", | 424 | "signup.password.label": "Mot de passe", |
422 | "signup.submit.label": "Créer un compte", | 425 | "signup.submit.label": "Créer un compte", |
423 | "tabs.item.confirmDeleteService": "Do you really want to delete the {serviceName} service?", | 426 | "tabs.item.confirmDeleteService": "Voulez-vous vraiment supprimer le service {serviceName}?", |
424 | "tabs.item.deleteService": "Delete service", | 427 | "tabs.item.deleteService": "Supprimer le service", |
425 | "tabs.item.disableAudio": "Désactiver l'audio", | 428 | "tabs.item.disableAudio": "Désactiver l'audio", |
426 | "tabs.item.disableDarkMode": "Disable Dark mode", | 429 | "tabs.item.disableDarkMode": "Désactiver le mode sombre", |
427 | "tabs.item.disableNotifications": "Désactiver les notifications", | 430 | "tabs.item.disableNotifications": "Désactiver les notifications", |
428 | "tabs.item.disableService": "Disable service", | 431 | "tabs.item.disableService": "Désactiver le service", |
429 | "tabs.item.enableAudio": "Activer l'audio", | 432 | "tabs.item.enableAudio": "Activer l'audio", |
430 | "tabs.item.enableDarkMode": "Enable Dark mode", | 433 | "tabs.item.enableDarkMode": "Activer le mode sombre", |
431 | "tabs.item.enableNotification": "Activer les notifications", | 434 | "tabs.item.enableNotification": "Activer les notifications", |
432 | "tabs.item.enableService": "Activer le service", | 435 | "tabs.item.enableService": "Activer le service", |
433 | "tabs.item.hibernateService": "Hibernate service", | 436 | "tabs.item.hibernateService": "Service de mise en veille prolongée", |
434 | "tabs.item.reload": "Actualiser", | 437 | "tabs.item.reload": "Actualiser", |
435 | "tabs.item.wakeUpService": "Wake up service", | 438 | "tabs.item.wakeUpService": "Service de réveil", |
436 | "validation.email": "{field} is not valid", | 439 | "validation.email": "{field}Â n'est pas valide", |
437 | "validation.minLength": "{field} should be at least {length} characters long", | 440 | "validation.minLength": "{field} doit comporter au moins {length} caractère(s)", |
438 | "validation.oneRequired": "Au moins un de ces champs est requis", | 441 | "validation.oneRequired": "Au moins un de ces champs est requis", |
439 | "validation.required": "{field} is required", | 442 | "validation.required": "{field} est requis", |
440 | "validation.url": "{field} is not a valid URL", | 443 | "validation.url": "{field} n'est pas une URL valide", |
441 | "webControls.back": "Revenir", | 444 | "webControls.back": "Revenir", |
442 | "webControls.forward": "Avancer", | 445 | "webControls.forward": "Avancer", |
443 | "webControls.goHome": "Accueil", | 446 | "webControls.goHome": "Accueil", |
@@ -445,12 +448,12 @@ | |||
445 | "webControls.reload": "Actualiser", | 448 | "webControls.reload": "Actualiser", |
446 | "welcome.loginButton": "Se connecter sur son compte", | 449 | "welcome.loginButton": "Se connecter sur son compte", |
447 | "welcome.signupButton": "Créer un compte gratuit", | 450 | "welcome.signupButton": "Créer un compte gratuit", |
448 | "workspaceDrawer.addNewWorkspaceLabel": "Add new workspace", | 451 | "workspaceDrawer.addNewWorkspaceLabel": "Ajouter un nouvel espace de travail", |
449 | "workspaceDrawer.allServices": "Tous les services", | 452 | "workspaceDrawer.allServices": "Tous les services", |
450 | "workspaceDrawer.headline": "Espace de travail", | 453 | "workspaceDrawer.headline": "Espace de travail", |
451 | "workspaceDrawer.item.contextMenuEdit": "Modifier", | 454 | "workspaceDrawer.item.contextMenuEdit": "Modifier", |
452 | "workspaceDrawer.item.noServicesAddedYet": "Aucun services ajoutés pour l'instant", | 455 | "workspaceDrawer.item.noServicesAddedYet": "Aucun services ajoutés pour l'instant", |
453 | "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", | 456 | "workspaceDrawer.workspaceFeatureInfo": "<p>Les Espaces de travail de Ferdi vous permettant de rester concentré sur ce qui est important. Créez différents groupes de services et naviguez facilement entre eux à n'importe quel moment.</p><p>Vous décidez de quels services vous avez besoin, où et quand, ainsi nous pouvons vous aider à rester concentré sur votre travail - ou à le quitter dès que vous le souhaitez.</p>", |
454 | "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", | 457 | "workspaceDrawer.workspacesSettingsTooltip": "Éditer les paramètres de l'espace de travail", |
455 | "workspaces.switchingIndicator.switchingTo": "Changement vers" | 458 | "workspaces.switchingIndicator.switchingTo": "Changement vers" |
456 | } | 459 | } |
diff --git a/src/i18n/locales/ga.json b/src/i18n/locales/ga.json index 46dc1acc4..ed893c943 100644 --- a/src/i18n/locales/ga.json +++ b/src/i18n/locales/ga.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Cumasaigh luasghéarú APG", | 212 | "settings.app.form.enableGPUAcceleration": "Cumasaigh luasghéarú APG", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Cumasaigh seiceáil litrithe", | 217 | "settings.app.form.enableSpellchecking": "Cumasaigh seiceáil litrithe", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Cumasaigh fógraÃ", | 312 | "settings.service.form.enableNotification": "Cumasaigh fógraÃ", |
311 | "settings.service.form.enableService": "Cumasaigh seirbhÃs", | 313 | "settings.service.form.enableService": "Cumasaigh seirbhÃs", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Comhartha do theachtaireachtaà neamhléite", | 315 | "settings.service.form.headlineBadges": "Comhartha do theachtaireachtaà neamhléite", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Ginearálta", | 317 | "settings.service.form.headlineGeneral": "Ginearálta", |
diff --git a/src/i18n/locales/he.json b/src/i18n/locales/he.json index 5bec0bf13..1ba7c5ada 100644 --- a/src/i18n/locales/he.json +++ b/src/i18n/locales/he.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "×פשר מצב לילה", | 211 | "settings.app.form.darkMode": "×פשר מצב לילה", |
212 | "settings.app.form.enableGPUAcceleration": "×פשר ×”×צת חומרה (GPU)", | 212 | "settings.app.form.enableGPUAcceleration": "×פשר ×”×צת חומרה (GPU)", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "×פשר × ×¢×™×œ×” ב×מצעות סיסמ×", | 214 | "settings.app.form.enableLock": "×פשר × ×¢×™×œ×” ב×מצעות סיסמ×", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "הפעל בדיקת ×יות", | 217 | "settings.app.form.enableSpellchecking": "הפעל בדיקת ×יות", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "×פשר התר×ות", | 312 | "settings.service.form.enableNotification": "×פשר התר×ות", |
311 | "settings.service.form.enableService": "×פשר שירות", | 313 | "settings.service.form.enableService": "×פשר שירות", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "חיווי על הודעות ×©×œ× × ×§×¨×ו", | 315 | "settings.service.form.headlineBadges": "חיווי על הודעות ×©×œ× × ×§×¨×ו", |
313 | "settings.service.form.headlineDarkReaderSettings": "הגדרות מצב לילה", | 316 | "settings.service.form.headlineDarkReaderSettings": "הגדרות מצב לילה", |
314 | "settings.service.form.headlineGeneral": "כללי", | 317 | "settings.service.form.headlineGeneral": "כללי", |
diff --git a/src/i18n/locales/hi.json b/src/i18n/locales/hi.json index 812ec8c4c..d5cf072f6 100644 --- a/src/i18n/locales/hi.json +++ b/src/i18n/locales/hi.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/hr.json b/src/i18n/locales/hr.json index 6dcd65032..6f0677e16 100644 --- a/src/i18n/locales/hr.json +++ b/src/i18n/locales/hr.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Omogući provjeru pravopisa", | 217 | "settings.app.form.enableSpellchecking": "Omogući provjeru pravopisa", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Omogućite obavijesti", | 312 | "settings.service.form.enableNotification": "Omogućite obavijesti", |
311 | "settings.service.form.enableService": "Omogućite usluge", | 313 | "settings.service.form.enableService": "Omogućite usluge", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Općenito", | 317 | "settings.service.form.headlineGeneral": "Općenito", |
diff --git a/src/i18n/locales/hu.json b/src/i18n/locales/hu.json index 8d83fea42..8a9c1a441 100644 --- a/src/i18n/locales/hu.json +++ b/src/i18n/locales/hu.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Sötét mód engedélyezése", | 211 | "settings.app.form.darkMode": "Sötét mód engedélyezése", |
212 | "settings.app.form.enableGPUAcceleration": "Hardveres gyorsÃtás engedélyezése", | 212 | "settings.app.form.enableGPUAcceleration": "Hardveres gyorsÃtás engedélyezése", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Jelszavas zár engedélyezése", | 214 | "settings.app.form.enableLock": "Jelszavas zár engedélyezése", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "HelyesÃrás-ellenÅ‘rzés engedélyezése", | 217 | "settings.app.form.enableSpellchecking": "HelyesÃrás-ellenÅ‘rzés engedélyezése", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "ÉrtesÃtések engedélyezése", | 312 | "settings.service.form.enableNotification": "ÉrtesÃtések engedélyezése", |
311 | "settings.service.form.enableService": "Szolgáltatás engedélyezése", | 313 | "settings.service.form.enableService": "Szolgáltatás engedélyezése", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Olvasatlan üzenet jelzések", | 315 | "settings.service.form.headlineBadges": "Olvasatlan üzenet jelzések", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Ãltalános", | 317 | "settings.service.form.headlineGeneral": "Ãltalános", |
diff --git a/src/i18n/locales/id.json b/src/i18n/locales/id.json index 5a2aa1ea7..a57b60247 100644 --- a/src/i18n/locales/id.json +++ b/src/i18n/locales/id.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Aktifkan Mode Gelap", | 211 | "settings.app.form.darkMode": "Aktifkan Mode Gelap", |
212 | "settings.app.form.enableGPUAcceleration": "Aktifkan Akselerasi GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Aktifkan Akselerasi GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Aktifkan pemeriksaan ejaan", | 217 | "settings.app.form.enableSpellchecking": "Aktifkan pemeriksaan ejaan", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Aktifkan pemberitahuan", | 312 | "settings.service.form.enableNotification": "Aktifkan pemberitahuan", |
311 | "settings.service.form.enableService": "Aktifkan layanan", | 313 | "settings.service.form.enableService": "Aktifkan layanan", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Lencana pesan belum dibaca", | 315 | "settings.service.form.headlineBadges": "Lencana pesan belum dibaca", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Umum", | 317 | "settings.service.form.headlineGeneral": "Umum", |
diff --git a/src/i18n/locales/it.json b/src/i18n/locales/it.json index 004bd2d26..09cfaaf25 100644 --- a/src/i18n/locales/it.json +++ b/src/i18n/locales/it.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Server Todo personalizzato", | 210 | "settings.app.form.customTodoServer": "Server Todo personalizzato", |
211 | "settings.app.form.darkMode": "Attiva la modalità scura.", | 211 | "settings.app.form.darkMode": "Attiva la modalità scura.", |
212 | "settings.app.form.enableGPUAcceleration": "Attiva Accelerazione GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Attiva Accelerazione GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Abilita blocco con password", | 214 | "settings.app.form.enableLock": "Abilita blocco con password", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Mostra sempre Ferdi nella barra dei menù", | 216 | "settings.app.form.enableMenuBar": "Mostra sempre Ferdi nella barra dei menù", |
215 | "settings.app.form.enableSpellchecking": "Attiva controllo ortografico", | 217 | "settings.app.form.enableSpellchecking": "Attiva controllo ortografico", |
216 | "settings.app.form.enableSystemTray": "Mostra sempre icona Ferdi in area di notifica", | 218 | "settings.app.form.enableSystemTray": "Mostra sempre icona Ferdi in area di notifica", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Abilita ibernazione", | 311 | "settings.service.form.enableHibernation": "Abilita ibernazione", |
310 | "settings.service.form.enableNotification": "Attiva le notifiche", | 312 | "settings.service.form.enableNotification": "Attiva le notifiche", |
311 | "settings.service.form.enableService": "Attiva il servizio", | 313 | "settings.service.form.enableService": "Attiva il servizio", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Etichetta dei messaggi non letti", | 315 | "settings.service.form.headlineBadges": "Etichetta dei messaggi non letti", |
313 | "settings.service.form.headlineDarkReaderSettings": "Impostazioni della Modalità Scura", | 316 | "settings.service.form.headlineDarkReaderSettings": "Impostazioni della Modalità Scura", |
314 | "settings.service.form.headlineGeneral": "Generale", | 317 | "settings.service.form.headlineGeneral": "Generale", |
diff --git a/src/i18n/locales/ja.json b/src/i18n/locales/ja.json index d07ed53af..54257c599 100644 --- a/src/i18n/locales/ja.json +++ b/src/i18n/locales/ja.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "カスタムToDoサーãƒãƒ¼", | 210 | "settings.app.form.customTodoServer": "カスタムToDoサーãƒãƒ¼", |
211 | "settings.app.form.darkMode": "ダークモードを有効ã«ã™ã‚‹", | 211 | "settings.app.form.darkMode": "ダークモードを有効ã«ã™ã‚‹", |
212 | "settings.app.form.enableGPUAcceleration": "GPUアクセラレーションを有効ã«ã™ã‚‹", | 212 | "settings.app.form.enableGPUAcceleration": "GPUアクセラレーションを有効ã«ã™ã‚‹", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "パスワードãƒãƒƒã‚¯ã‚’有効ã«ã™ã‚‹", | 214 | "settings.app.form.enableLock": "パスワードãƒãƒƒã‚¯ã‚’有効ã«ã™ã‚‹", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Ferdi内ã«ãƒ¡ãƒ‹ãƒ¥ãƒ¼ãƒãƒ¼ã‚’常時表示ã™ã‚‹", | 216 | "settings.app.form.enableMenuBar": "Ferdi内ã«ãƒ¡ãƒ‹ãƒ¥ãƒ¼ãƒãƒ¼ã‚’常時表示ã™ã‚‹", |
215 | "settings.app.form.enableSpellchecking": "スペルãƒã‚§ãƒƒã‚¯ã‚’有効ã«ã™ã‚‹", | 217 | "settings.app.form.enableSpellchecking": "スペルãƒã‚§ãƒƒã‚¯ã‚’有効ã«ã™ã‚‹", |
216 | "settings.app.form.enableSystemTray": "Ferdiを常時システムトレイã«è¡¨ç¤ºã™ã‚‹", | 218 | "settings.app.form.enableSystemTray": "Ferdiを常時システムトレイã«è¡¨ç¤ºã™ã‚‹", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "休æ¢ã‚’有効ã«ã™ã‚‹", | 311 | "settings.service.form.enableHibernation": "休æ¢ã‚’有効ã«ã™ã‚‹", |
310 | "settings.service.form.enableNotification": "通知を有効ã«ã™ã‚‹", | 312 | "settings.service.form.enableNotification": "通知を有効ã«ã™ã‚‹", |
311 | "settings.service.form.enableService": "サービスを有効ã«ã™ã‚‹", | 313 | "settings.service.form.enableService": "サービスを有効ã«ã™ã‚‹", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "未èªä»¶æ•°ã®é€šçŸ¥ãƒãƒƒã‚¸", | 315 | "settings.service.form.headlineBadges": "未èªä»¶æ•°ã®é€šçŸ¥ãƒãƒƒã‚¸", |
313 | "settings.service.form.headlineDarkReaderSettings": "ダークリーダーè¨å®š", | 316 | "settings.service.form.headlineDarkReaderSettings": "ダークリーダーè¨å®š", |
314 | "settings.service.form.headlineGeneral": "一般", | 317 | "settings.service.form.headlineGeneral": "一般", |
diff --git a/src/i18n/locales/ka.json b/src/i18n/locales/ka.json index f327c1e51..fee1f6668 100644 --- a/src/i18n/locales/ka.json +++ b/src/i18n/locales/ka.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "შეტყáƒáƒ‘ინებების ჩáƒáƒ თვáƒ", | 312 | "settings.service.form.enableNotification": "შეტყáƒáƒ‘ინებების ჩáƒáƒ თვáƒ", |
311 | "settings.service.form.enableService": "სერვისის ჩáƒáƒ თვáƒ", | 313 | "settings.service.form.enableService": "სერვისის ჩáƒáƒ თვáƒ", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "მთáƒáƒ•áƒáƒ ი", | 317 | "settings.service.form.headlineGeneral": "მთáƒáƒ•áƒáƒ ი", |
diff --git a/src/i18n/locales/ko.json b/src/i18n/locales/ko.json index 3be2b27ff..c66313751 100644 --- a/src/i18n/locales/ko.json +++ b/src/i18n/locales/ko.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "ë‹¤í¬ ëª¨ë“œ 활성화", | 211 | "settings.app.form.darkMode": "ë‹¤í¬ ëª¨ë“œ 활성화", |
212 | "settings.app.form.enableGPUAcceleration": "GPU ê°€ì† ì‚¬ìš©", | 212 | "settings.app.form.enableGPUAcceleration": "GPU ê°€ì† ì‚¬ìš©", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "암호 ìž ê¸ˆ 활성화", | 214 | "settings.app.form.enableLock": "암호 ìž ê¸ˆ 활성화", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "맞춤법 검사 활성화", | 217 | "settings.app.form.enableSpellchecking": "맞춤법 검사 활성화", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "ì ˆì „ëª¨ë“œ 활성화", | 311 | "settings.service.form.enableHibernation": "ì ˆì „ëª¨ë“œ 활성화", |
310 | "settings.service.form.enableNotification": "알림 허용", | 312 | "settings.service.form.enableNotification": "알림 허용", |
311 | "settings.service.form.enableService": "서비스 활성화", | 313 | "settings.service.form.enableService": "서비스 활성화", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "ì½ì§€ ì•Šì€ ë©”ì„¸ì§€ 배지 표시", | 315 | "settings.service.form.headlineBadges": "ì½ì§€ ì•Šì€ ë©”ì„¸ì§€ 배지 표시", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "ì¼ë°˜", | 317 | "settings.service.form.headlineGeneral": "ì¼ë°˜", |
diff --git a/src/i18n/locales/nl-BE.json b/src/i18n/locales/nl-BE.json index 5104d1bc2..1cd2cdc42 100644 --- a/src/i18n/locales/nl-BE.json +++ b/src/i18n/locales/nl-BE.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Dark Mode aanzetten", | 211 | "settings.app.form.darkMode": "Dark Mode aanzetten", |
212 | "settings.app.form.enableGPUAcceleration": "GPU Acceleratie Activeren", | 212 | "settings.app.form.enableGPUAcceleration": "GPU Acceleratie Activeren", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Spellingcontrole inschakelen", | 217 | "settings.app.form.enableSpellchecking": "Spellingcontrole inschakelen", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Notificaties aanzetten", | 312 | "settings.service.form.enableNotification": "Notificaties aanzetten", |
311 | "settings.service.form.enableService": "Service aanzetten", | 313 | "settings.service.form.enableService": "Service aanzetten", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Ongelezen berichten badges", | 315 | "settings.service.form.headlineBadges": "Ongelezen berichten badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Algemeen", | 317 | "settings.service.form.headlineGeneral": "Algemeen", |
diff --git a/src/i18n/locales/nl.json b/src/i18n/locales/nl.json index 6899e02ee..4d58e5611 100644 --- a/src/i18n/locales/nl.json +++ b/src/i18n/locales/nl.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Aangepaste Todo Server", | 210 | "settings.app.form.customTodoServer": "Aangepaste Todo Server", |
211 | "settings.app.form.darkMode": "Dark mode aanzetten", | 211 | "settings.app.form.darkMode": "Dark mode aanzetten", |
212 | "settings.app.form.enableGPUAcceleration": "Schakel videokaart-acceleratie in ", | 212 | "settings.app.form.enableGPUAcceleration": "Schakel videokaart-acceleratie in ", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Wachtwoordvergrendeling inschakelen", | 214 | "settings.app.form.enableLock": "Wachtwoordvergrendeling inschakelen", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Altijd Ferdi weergeven in de menubalk", | 216 | "settings.app.form.enableMenuBar": "Altijd Ferdi weergeven in de menubalk", |
215 | "settings.app.form.enableSpellchecking": "Zet spellingcontrole aan", | 217 | "settings.app.form.enableSpellchecking": "Zet spellingcontrole aan", |
216 | "settings.app.form.enableSystemTray": "Laat Ferdi altijd in systeembalk zien", | 218 | "settings.app.form.enableSystemTray": "Laat Ferdi altijd in systeembalk zien", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Meldingen inschakelen", | 312 | "settings.service.form.enableNotification": "Meldingen inschakelen", |
311 | "settings.service.form.enableService": "Service inschakelen", | 313 | "settings.service.form.enableService": "Service inschakelen", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Ongelezen berichten badges", | 315 | "settings.service.form.headlineBadges": "Ongelezen berichten badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Algemeen", | 317 | "settings.service.form.headlineGeneral": "Algemeen", |
diff --git a/src/i18n/locales/no.json b/src/i18n/locales/no.json index cfa523a35..0d927a6cd 100644 --- a/src/i18n/locales/no.json +++ b/src/i18n/locales/no.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Aktiver mørkt tema", | 211 | "settings.app.form.darkMode": "Aktiver mørkt tema", |
212 | "settings.app.form.enableGPUAcceleration": "Aktiver GPU-akselerasjon", | 212 | "settings.app.form.enableGPUAcceleration": "Aktiver GPU-akselerasjon", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Aktiver passordlås", | 214 | "settings.app.form.enableLock": "Aktiver passordlås", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Aktiver stavekontroll", | 217 | "settings.app.form.enableSpellchecking": "Aktiver stavekontroll", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Aktiver varsler", | 312 | "settings.service.form.enableNotification": "Aktiver varsler", |
311 | "settings.service.form.enableService": "Aktivere tjenesten", | 313 | "settings.service.form.enableService": "Aktivere tjenesten", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Uleste meldingsetiketter", | 315 | "settings.service.form.headlineBadges": "Uleste meldingsetiketter", |
313 | "settings.service.form.headlineDarkReaderSettings": "Innstillinger for lesing i mørket", | 316 | "settings.service.form.headlineDarkReaderSettings": "Innstillinger for lesing i mørket", |
314 | "settings.service.form.headlineGeneral": "Generelt", | 317 | "settings.service.form.headlineGeneral": "Generelt", |
diff --git a/src/i18n/locales/pl.json b/src/i18n/locales/pl.json index a19c7c444..b6c3bc98c 100644 --- a/src/i18n/locales/pl.json +++ b/src/i18n/locales/pl.json | |||
@@ -1,6 +1,6 @@ | |||
1 | { | 1 | { |
2 | "app.errorHandler.action": "Odśwież", | 2 | "app.errorHandler.action": "Odśwież", |
3 | "app.errorHandler.headline": "Coś poszło nie tak.", | 3 | "app.errorHandler.headline": "Something went wrong.", |
4 | "changeserver.customServerLabel": "Spersonalizowany serwer", | 4 | "changeserver.customServerLabel": "Spersonalizowany serwer", |
5 | "changeserver.headline": "Zmień serwer", | 5 | "changeserver.headline": "Zmień serwer", |
6 | "changeserver.label": "Serwer", | 6 | "changeserver.label": "Serwer", |
@@ -22,15 +22,15 @@ | |||
22 | "feature.publishDebugInfo.title": "Opublikuj dane diagnostyczne", | 22 | "feature.publishDebugInfo.title": "Opublikuj dane diagnostyczne", |
23 | "feature.quickSwitch.info": "Wybierz usługę naciskając TAB, ↑ oraz ↓. Otwórz usługę naciskając ENTER.", | 23 | "feature.quickSwitch.info": "Wybierz usługę naciskając TAB, ↑ oraz ↓. Otwórz usługę naciskając ENTER.", |
24 | "feature.quickSwitch.search": "Szukaj...", | 24 | "feature.quickSwitch.search": "Szukaj...", |
25 | "feature.quickSwitch.title": "Szybkie przełączenie", | 25 | "feature.quickSwitch.title": "QuickSwitch", |
26 | "global.api.unhealthy": "Nie można połączyć z usługami Ferdi", | 26 | "global.api.unhealthy": "Can't connect to Ferdi online services", |
27 | "global.cancel": "Anuluj", | 27 | "global.cancel": "Anuluj", |
28 | "global.edit": "Edytuj", | 28 | "global.edit": "Edytuj", |
29 | "global.no": "Nie", | 29 | "global.no": "Nie", |
30 | "global.notConnectedToTheInternet": "Nie masz połączenia z Internetem.", | 30 | "global.notConnectedToTheInternet": "Nie masz połączenia z Internetem.", |
31 | "global.ok": "Ok", | 31 | "global.ok": "Ok", |
32 | "global.quit": "Wyjdź", | 32 | "global.quit": "Wyjdź", |
33 | "global.quitConfirmation": "Czy chcesz wyjść z Ferdi?", | 33 | "global.quitConfirmation": "Czy napewno chcesz wyjść z Ferdi?", |
34 | "global.save": "Zapisz", | 34 | "global.save": "Zapisz", |
35 | "global.settings": "Ustawienia", | 35 | "global.settings": "Ustawienia", |
36 | "global.spellchecker.useDefault": "Użyj domyślnego dla systemu ({default})", | 36 | "global.spellchecker.useDefault": "Użyj domyślnego dla systemu ({default})", |
@@ -44,9 +44,9 @@ | |||
44 | "import.headline": "Importuj usługi Ferdi 4", | 44 | "import.headline": "Importuj usługi Ferdi 4", |
45 | "import.notSupportedHeadline": "Usługi, które nie są jeszcze obsługiwane w Ferdi 5", | 45 | "import.notSupportedHeadline": "Usługi, które nie są jeszcze obsługiwane w Ferdi 5", |
46 | "import.skip.label": "Chcę dodać usługi samodzielnie", | 46 | "import.skip.label": "Chcę dodać usługi samodzielnie", |
47 | "import.submit.label": "Importuj {count} usług", | 47 | "import.submit.label": "Import {count} services", |
48 | "infobar.authRequestFailed": "Pojawiły się błędy podczas próby uwierzytelniania. Proszę spróbuj się wylogować i zalogować ponownie, jeśli ten błąd będzie się powtarzał.", | 48 | "infobar.authRequestFailed": "Pojawiły się błędy podczas próby uwierzytelniania. Proszę spróbuj się wylogować i zalogować ponownie, jeśli ten błąd będzie się powtarzał.", |
49 | "infobar.buttonChangelog": "Co nowego?", | 49 | "infobar.buttonChangelog": "What is new?", |
50 | "infobar.buttonInstallUpdate": "Uruchom ponownie i zainstaluj aktualizacjÄ™", | 50 | "infobar.buttonInstallUpdate": "Uruchom ponownie i zainstaluj aktualizacjÄ™", |
51 | "infobar.buttonReloadServices": "Odśwież usługi", | 51 | "infobar.buttonReloadServices": "Odśwież usługi", |
52 | "infobar.hide": "Ukryj", | 52 | "infobar.hide": "Ukryj", |
@@ -69,13 +69,13 @@ | |||
69 | "locked.touchIdPrompt": "odblokuj przez Touch ID", | 69 | "locked.touchIdPrompt": "odblokuj przez Touch ID", |
70 | "locked.unlockWithPassword": "Odblokuj hasłem", | 70 | "locked.unlockWithPassword": "Odblokuj hasłem", |
71 | "login.changeServer": "Zmień serwer", | 71 | "login.changeServer": "Zmień serwer", |
72 | "login.customServerQuestion": "Używasz niestandardowego serwera Ferdi?", | 72 | "login.customServerQuestion": "Using a custom Ferdi server?", |
73 | "login.customServerSuggestion": "Spróbuj zaimportować konto Franz", | 73 | "login.customServerSuggestion": "Try importing your Franz account", |
74 | "login.email.label": "Adres email", | 74 | "login.email.label": "Adres email", |
75 | "login.headline": "Zaloguj siÄ™", | 75 | "login.headline": "Zaloguj siÄ™", |
76 | "login.invalidCredentials": "Adres email lub hasło są błędne", | 76 | "login.invalidCredentials": "Adres email lub hasło są błędne", |
77 | "login.link.password": "Resetuj hasło", | 77 | "login.link.password": "Reset password", |
78 | "login.link.signup": "Załóż konto", | 78 | "login.link.signup": "Załóż darmowe konto", |
79 | "login.password.label": "Hasło", | 79 | "login.password.label": "Hasło", |
80 | "login.serverLogout": "Twoja sesja wygasła, zaloguj się ponownie.", | 80 | "login.serverLogout": "Twoja sesja wygasła, zaloguj się ponownie.", |
81 | "login.submit.label": "Zaloguj siÄ™", | 81 | "login.submit.label": "Zaloguj siÄ™", |
@@ -116,17 +116,17 @@ | |||
116 | "menu.help.support": "Wsparcie", | 116 | "menu.help.support": "Wsparcie", |
117 | "menu.help.tos": "Warunki użytkowania", | 117 | "menu.help.tos": "Warunki użytkowania", |
118 | "menu.services": "Usługi", | 118 | "menu.services": "Usługi", |
119 | "menu.services.activatePreviousService": "Aktywuj poprzednią usługę", | 119 | "menu.services.activatePreviousService": "Activate previous service", |
120 | "menu.services.addNewService": "Dodaj nową usługę...", | 120 | "menu.services.addNewService": "Add New Service...", |
121 | "menu.services.goHome": "Strona główna", | 121 | "menu.services.goHome": "Strona główna", |
122 | "menu.services.setNextServiceActive": "Aktywuj następną usługę", | 122 | "menu.services.setNextServiceActive": "Activate next service", |
123 | "menu.todos": "Lista zadań", | 123 | "menu.todos": "Lista zadań", |
124 | "menu.todos.enableTodos": "Włącz listę zadań", | 124 | "menu.todos.enableTodos": "Włącz listę zadań", |
125 | "menu.view": "Widok", | 125 | "menu.view": "Widok", |
126 | "menu.view.back": "Wstecz", | 126 | "menu.view.back": "Wstecz", |
127 | "menu.view.forward": "Prześlij dalej", | 127 | "menu.view.forward": "Prześlij dalej", |
128 | "menu.view.lockFerdi": "Zablokuj Ferdi", | 128 | "menu.view.lockFerdi": "Zablokuj Ferdi", |
129 | "menu.view.openQuickSwitch": "Otwórz szybki wybór", | 129 | "menu.view.openQuickSwitch": "Otwórz Quick Switch", |
130 | "menu.view.reloadFerdi": "Przeładuj Ferdi", | 130 | "menu.view.reloadFerdi": "Przeładuj Ferdi", |
131 | "menu.view.reloadService": "Przeładuj usługę", | 131 | "menu.view.reloadService": "Przeładuj usługę", |
132 | "menu.view.reloadTodos": "Odśwież Zadania", | 132 | "menu.view.reloadTodos": "Odśwież Zadania", |
@@ -147,11 +147,11 @@ | |||
147 | "menu.workspaces.defaultWorkspace": "Wszystkie usługi", | 147 | "menu.workspaces.defaultWorkspace": "Wszystkie usługi", |
148 | "menu.workspaces.openWorkspaceDrawer": "Otwórz edytor obszaru roboczego", | 148 | "menu.workspaces.openWorkspaceDrawer": "Otwórz edytor obszaru roboczego", |
149 | "password.email.label": "Adres email", | 149 | "password.email.label": "Adres email", |
150 | "password.headline": "Resetuj hasło", | 150 | "password.headline": "Reset password", |
151 | "password.link.login": "Zaloguj siÄ™ na swoje konto", | 151 | "password.link.login": "Zaloguj siÄ™ na swoje konto", |
152 | "password.link.signup": "Załóż darmowe konto", | 152 | "password.link.signup": "Załóż darmowe konto", |
153 | "password.noUser": "Użytkownik z podanym adresem email nie istnieje", | 153 | "password.noUser": "No user with that email address was found", |
154 | "password.successInfo": "Twoje nowe hasło zostało wysłane na Twoją skrzynkę mailową", | 154 | "password.successInfo": "Your new password was sent to your email address", |
155 | "service.crashHandler.action": "Przeładuj {name}", | 155 | "service.crashHandler.action": "Przeładuj {name}", |
156 | "service.crashHandler.autoReload": "Próba automatycznego odnowienia {name} za {seconds} sekund/y", | 156 | "service.crashHandler.autoReload": "Próba automatycznego odnowienia {name} za {seconds} sekund/y", |
157 | "service.crashHandler.headline": "O nie!", | 157 | "service.crashHandler.headline": "O nie!", |
@@ -166,10 +166,10 @@ | |||
166 | "service.webviewLoader.loading": "Wczytywanie {service}", | 166 | "service.webviewLoader.loading": "Wczytywanie {service}", |
167 | "services.getStarted": "Zacznij", | 167 | "services.getStarted": "Zacznij", |
168 | "services.login": "Zaloguj się, aby używać Ferdi.", | 168 | "services.login": "Zaloguj się, aby używać Ferdi.", |
169 | "services.serverInfo": "Opcjonalnie możesz zmienić swój serwer Ferdi klikając zębatkę w lewym dolnym rogu. Jeżeli przełączasz się (z jednego z hostowanych serwerów) do serwerów Ferdi bez konta, pamiętaj, że możesz wyeksportować swoje dane z tego serwera a następnie zaimportować je używając opcji Pomoc w menu aby przywrócić swoje obszary robocze i skonfigurowane usługi!", | 169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", |
170 | "services.serverless": "Używaj Ferdi bez konta", | 170 | "services.serverless": "Używaj Ferdi bez konta", |
171 | "services.welcome": "Witaj w programie Ferdi", | 171 | "services.welcome": "Witaj w programie Ferdi", |
172 | "settings.account.account.editButton": "Edytuj konto", | 172 | "settings.account.account.editButton": "Edit account", |
173 | "settings.account.accountUnavailable": "Konto jest niedostępne", | 173 | "settings.account.accountUnavailable": "Konto jest niedostępne", |
174 | "settings.account.accountUnavailableInfo": "Używasz Ferdi bez konta. Jeśli chcesz używać Ferdi z kontem i synchronizować swoje usługi pomiędzy klientami, wybierz serwer w zakładce Ustawienia i zaloguj się.", | 174 | "settings.account.accountUnavailableInfo": "Używasz Ferdi bez konta. Jeśli chcesz używać Ferdi z kontem i synchronizować swoje usługi pomiędzy klientami, wybierz serwer w zakładce Ustawienia i zaloguj się.", |
175 | "settings.account.buttonSave": "Uaktualnij profil", | 175 | "settings.account.buttonSave": "Uaktualnij profil", |
@@ -177,21 +177,21 @@ | |||
177 | "settings.account.deleteEmailSent": "Wysłaliśmy email z linkiem do potwierdzenia usunięcia konta. Konto oraz dane są usuwane trwale i nie można tego cofnąć!", | 177 | "settings.account.deleteEmailSent": "Wysłaliśmy email z linkiem do potwierdzenia usunięcia konta. Konto oraz dane są usuwane trwale i nie można tego cofnąć!", |
178 | "settings.account.deleteInfo": "Jeżeli nie potrzebujesz już konta Ferdi, możesz je usunąć oraz wszystkie dane na nim zapisane.", | 178 | "settings.account.deleteInfo": "Jeżeli nie potrzebujesz już konta Ferdi, możesz je usunąć oraz wszystkie dane na nim zapisane.", |
179 | "settings.account.headline": "Konto", | 179 | "settings.account.headline": "Konto", |
180 | "settings.account.headlineAccount": "Informacje o koncie", | 180 | "settings.account.headlineAccount": "Account information", |
181 | "settings.account.headlineDangerZone": "Strefa niebezpieczna", | 181 | "settings.account.headlineDangerZone": "Danger Zone", |
182 | "settings.account.headlineInvoices": "Faktury", | 182 | "settings.account.headlineInvoices": "Invoices", |
183 | "settings.account.headlinePassword": "Zmień hasło", | 183 | "settings.account.headlinePassword": "Change password", |
184 | "settings.account.headlineProfile": "Uaktualnij profil", | 184 | "settings.account.headlineProfile": "Uaktualnij profil", |
185 | "settings.account.successInfo": "Twoje zmiany zostały zapisane", | 185 | "settings.account.successInfo": "Twoje zmiany zostały zapisane", |
186 | "settings.account.tryReloadServices": "Spróbuj ponownie", | 186 | "settings.account.tryReloadServices": "Spróbuj ponownie", |
187 | "settings.account.tryReloadUserInfoRequest": "Spróbuj ponownie", | 187 | "settings.account.tryReloadUserInfoRequest": "Spróbuj ponownie", |
188 | "settings.account.userInfoRequestFailed": "Nie można wczytać informacji o użytkowniku", | 188 | "settings.account.userInfoRequestFailed": "Nie można wczytać informacji o użytkowniku", |
189 | "settings.account.yourLicense": " Twoja licencja Ferdi:", | 189 | "settings.account.yourLicense": "Your Ferdi License:", |
190 | "settings.app.accentColorInfo": "Zdefiniuj kolor akcentu w formacie zgodnym z CSS. (Domyślnie: {defaultAccentColor})", | 190 | "settings.app.accentColorInfo": "Zdefiniuj kolor akcentu w formacie zgodnym z CSS. (Domyślnie: {defaultAccentColor})", |
191 | "settings.app.buttonClearAllCache": "Wyczyść pamięć podręczną (cache)", | 191 | "settings.app.buttonClearAllCache": "Wyczyść pamięć podręczną (cache)", |
192 | "settings.app.buttonInstallUpdate": "Uruchom ponownie i zainstaluj aktualizacjÄ™", | 192 | "settings.app.buttonInstallUpdate": "Uruchom ponownie i zainstaluj aktualizacjÄ™", |
193 | "settings.app.buttonOpenFerdiProfileFolder": "Pokaż folder z profilami", | 193 | "settings.app.buttonOpenFerdiProfileFolder": "Pokaż folder z profilami", |
194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Otwórz folder usług", | 194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Open Service Recipes folder", |
195 | "settings.app.buttonSearchForUpdate": "Sprawdź aktualizacje", | 195 | "settings.app.buttonSearchForUpdate": "Sprawdź aktualizacje", |
196 | "settings.app.cacheInfo": "Pamięć podręczna zajmuje obecnie {size} przestrzeni dyskowej", | 196 | "settings.app.cacheInfo": "Pamięć podręczna zajmuje obecnie {size} przestrzeni dyskowej", |
197 | "settings.app.cacheNotCleared": "Błąd czyszczenia pamięci podręcznej", | 197 | "settings.app.cacheNotCleared": "Błąd czyszczenia pamięci podręcznej", |
@@ -207,24 +207,26 @@ | |||
207 | "settings.app.form.clipboardNotifications": "Nie wyświetlaj powiadomień dla schowka", | 207 | "settings.app.form.clipboardNotifications": "Nie wyświetlaj powiadomień dla schowka", |
208 | "settings.app.form.closeToSystemTray": "Zminimalizuj Ferdi do paska zadań", | 208 | "settings.app.form.closeToSystemTray": "Zminimalizuj Ferdi do paska zadań", |
209 | "settings.app.form.confirmOnQuit": "Potwierdź podczas opuszczania Ferdi", | 209 | "settings.app.form.confirmOnQuit": "Potwierdź podczas opuszczania Ferdi", |
210 | "settings.app.form.customTodoServer": "Niestandardowy serwer Todo", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "WÅ‚Ä…cz Ciemny motyw", | 211 | "settings.app.form.darkMode": "WÅ‚Ä…cz Ciemny motyw", |
212 | "settings.app.form.enableGPUAcceleration": "WÅ‚Ä…cz akceleracjÄ™ GPU", | 212 | "settings.app.form.enableGPUAcceleration": "WÅ‚Ä…cz akceleracjÄ™ GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Włącz blokadę hasłem", | 214 | "settings.app.form.enableLock": "Włącz blokadę hasłem", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Zawsze wyświetlaj Ferdi na pasku zadań", | 216 | "settings.app.form.enableMenuBar": "Zawsze wyświetlaj Ferdi na pasku zadań", |
215 | "settings.app.form.enableSpellchecking": "WÅ‚Ä…cz sprawdzanie pisowni", | 217 | "settings.app.form.enableSpellchecking": "WÅ‚Ä…cz sprawdzanie pisowni", |
216 | "settings.app.form.enableSystemTray": "Zawsze pokazuj Ferdi na pasku zadań", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
217 | "settings.app.form.enableTodos": "WÅ‚Ä…cz Zadania Ferdi", | 219 | "settings.app.form.enableTodos": "WÅ‚Ä…cz Zadania Ferdi", |
218 | "settings.app.form.hibernateOnStartup": "Hibernacja usług po starcie systemu", | 220 | "settings.app.form.hibernateOnStartup": "Hibernacja usług po starcie systemu", |
219 | "settings.app.form.hibernationStrategy": "Strategia hibernacji", | 221 | "settings.app.form.hibernationStrategy": "Strategia hibernacji", |
220 | "settings.app.form.iconSize": "Rozmiar ikony usługi", | 222 | "settings.app.form.iconSize": "Rozmiar ikony usługi", |
221 | "settings.app.form.inactivityLock": "Zablokuj przy braku aktywności", | 223 | "settings.app.form.inactivityLock": "Zablokuj przy braku aktywności", |
222 | "settings.app.form.keepAllWorkspacesLoaded": "Trzymaj wszystkie pola robocze załadowane", | 224 | "settings.app.form.keepAllWorkspacesLoaded": "Keep all workspaces loaded", |
223 | "settings.app.form.language": "Język", | 225 | "settings.app.form.language": "Język", |
224 | "settings.app.form.lockPassword": "Hasło", | 226 | "settings.app.form.lockPassword": "Hasło", |
225 | "settings.app.form.minimizeToSystemTray": "Zminimalizuj aplikacjÄ™ Ferdi", | 227 | "settings.app.form.minimizeToSystemTray": "Zminimalizuj aplikacjÄ™ Ferdi", |
226 | "settings.app.form.navigationBarBehaviour": "Zachowanie paska nawigacji", | 228 | "settings.app.form.navigationBarBehaviour": "Zachowanie paska nawigacji", |
227 | "settings.app.form.notifyTaskBarOnMessage": "Powiadom na pasku zadań o nowej wiadomości", | 229 | "settings.app.form.notifyTaskBarOnMessage": "Notify TaskBar/Dock on new message", |
228 | "settings.app.form.passwordToggle": "Przełącznik hasła", | 230 | "settings.app.form.passwordToggle": "Przełącznik hasła", |
229 | "settings.app.form.predefinedTodoServer": "Serwer Todo", | 231 | "settings.app.form.predefinedTodoServer": "Serwer Todo", |
230 | "settings.app.form.privateNotifications": "Nie pokazuj treści wiadomości w powiadomieniach", | 232 | "settings.app.form.privateNotifications": "Nie pokazuj treści wiadomości w powiadomieniach", |
@@ -237,12 +239,12 @@ | |||
237 | "settings.app.form.sentry": "Wysyłaj dane telemetrii", | 239 | "settings.app.form.sentry": "Wysyłaj dane telemetrii", |
238 | "settings.app.form.serviceRibbonWidth": "Szerokość paska bocznego", | 240 | "settings.app.form.serviceRibbonWidth": "Szerokość paska bocznego", |
239 | "settings.app.form.showDisabledServices": "Wyświetlaj karty wyłączonych usług", | 241 | "settings.app.form.showDisabledServices": "Wyświetlaj karty wyłączonych usług", |
240 | "settings.app.form.showDragArea": "Pokaż obszar do przeciągania w oknie", | 242 | "settings.app.form.showDragArea": "Show draggable area on window", |
241 | "settings.app.form.showMessagesBadgesWhenMuted": "Pokaż licznik nieprzeczytanych wiadomości gdy powiadomienia są wyłączone", | 243 | "settings.app.form.showMessagesBadgesWhenMuted": "Pokaż licznik nieprzeczytanych wiadomości gdy powiadomienia są wyłączone", |
242 | "settings.app.form.splitMode": "Aktywuj tryb podzielonego widoku", | 244 | "settings.app.form.splitMode": "Enable Split View Mode", |
243 | "settings.app.form.startMinimized": "Uruchom zminimalizowany", | 245 | "settings.app.form.startMinimized": "Uruchom zminimalizowany", |
244 | "settings.app.form.universalDarkMode": "WÅ‚Ä…cz uniwersalny tryb ciemny", | 246 | "settings.app.form.universalDarkMode": "WÅ‚Ä…cz uniwersalny tryb ciemny", |
245 | "settings.app.form.useTouchIdToUnlock": "Zawsze używaj TouchID do odblokowania Ferdi", | 247 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", |
246 | "settings.app.form.useVerticalStyle": "Użyj stylu poziomego", | 248 | "settings.app.form.useVerticalStyle": "Użyj stylu poziomego", |
247 | "settings.app.form.wakeUpStrategy": "Strategia wybudzania", | 249 | "settings.app.form.wakeUpStrategy": "Strategia wybudzania", |
248 | "settings.app.headlineAdvanced": "Zaawansowane", | 250 | "settings.app.headlineAdvanced": "Zaawansowane", |
@@ -251,8 +253,8 @@ | |||
251 | "settings.app.headlineLanguage": "Język", | 253 | "settings.app.headlineLanguage": "Język", |
252 | "settings.app.headlinePrivacy": "Prywatność", | 254 | "settings.app.headlinePrivacy": "Prywatność", |
253 | "settings.app.headlineUpdates": "Aktualizacje", | 255 | "settings.app.headlineUpdates": "Aktualizacje", |
254 | "settings.app.hibernateInfo": "Domyślnie, Ferdi trzyma wszystkie Twoje usługi włączone w tle, aby były gotowe w momencie kiedy będziesz ich potrzebował. Hibernacja usług odłączy Twoje usługi po określonym czasie. Dzięki temu zachowasz pamięć RAM lub zachowasz usługi od spowolnienia komputera.", | 256 | "settings.app.hibernateInfo": "By default, Ferdi will keep all your services open and loaded in the background so they are ready when you want to use them. Service Hibernation will unload your services after a specified amount. This is useful to save RAM or keeping services from slowing down your computer.", |
255 | "settings.app.inactivityLockInfo": "Minut do dezaktywacji, po których Ferdi automatycznie zablokuje. Wybierz 0 aby dezaktywować", | 257 | "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable", |
256 | "settings.app.languageDisclaimer": "Oficjalnymi językami są Angielski i Niemiecki. Inne języki są tłumaczone przez społeczność Ferdi.", | 258 | "settings.app.languageDisclaimer": "Oficjalnymi językami są Angielski i Niemiecki. Inne języki są tłumaczone przez społeczność Ferdi.", |
257 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", | 259 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", |
258 | "settings.app.lockedPassword": "Hasło", | 260 | "settings.app.lockedPassword": "Hasło", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "WÅ‚Ä…cz hibernacjÄ™", | 311 | "settings.service.form.enableHibernation": "WÅ‚Ä…cz hibernacjÄ™", |
310 | "settings.service.form.enableNotification": "Aktywuj powiadomienia", | 312 | "settings.service.form.enableNotification": "Aktywuj powiadomienia", |
311 | "settings.service.form.enableService": "Aktywuj usługę", | 313 | "settings.service.form.enableService": "Aktywuj usługę", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Znaczniki nieprzeczytanych wiadomości", | 315 | "settings.service.form.headlineBadges": "Znaczniki nieprzeczytanych wiadomości", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Ogólne", | 317 | "settings.service.form.headlineGeneral": "Ogólne", |
diff --git a/src/i18n/locales/pt-BR.json b/src/i18n/locales/pt-BR.json index 1d068e87e..08a631c9a 100644 --- a/src/i18n/locales/pt-BR.json +++ b/src/i18n/locales/pt-BR.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Servidor de Tarefas Personalizado", | 210 | "settings.app.form.customTodoServer": "Servidor de Tarefas Personalizado", |
211 | "settings.app.form.darkMode": "Ativar o Tema Escuro", | 211 | "settings.app.form.darkMode": "Ativar o Tema Escuro", |
212 | "settings.app.form.enableGPUAcceleration": "Ativar Aceleração de GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Ativar Aceleração de GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Habilitar bloqueio por senha", | 214 | "settings.app.form.enableLock": "Habilitar bloqueio por senha", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Sempre exibir Ferdi na Barra de Menu", | 216 | "settings.app.form.enableMenuBar": "Sempre exibir Ferdi na Barra de Menu", |
215 | "settings.app.form.enableSpellchecking": "Ativar verificação ortográfica", | 217 | "settings.app.form.enableSpellchecking": "Ativar verificação ortográfica", |
216 | "settings.app.form.enableSystemTray": "Sempre mostrar o Ãcone na Bandeja de Sistema", | 218 | "settings.app.form.enableSystemTray": "Sempre mostrar o Ãcone na Bandeja de Sistema", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Ativer hibernação", | 311 | "settings.service.form.enableHibernation": "Ativer hibernação", |
310 | "settings.service.form.enableNotification": "Ativar notificações", | 312 | "settings.service.form.enableNotification": "Ativar notificações", |
311 | "settings.service.form.enableService": "Ativar serviço", | 313 | "settings.service.form.enableService": "Ativar serviço", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Emblema de mensagem não lida", | 315 | "settings.service.form.headlineBadges": "Emblema de mensagem não lida", |
313 | "settings.service.form.headlineDarkReaderSettings": "Configurações do Dark Reader", | 316 | "settings.service.form.headlineDarkReaderSettings": "Configurações do Dark Reader", |
314 | "settings.service.form.headlineGeneral": "Geral", | 317 | "settings.service.form.headlineGeneral": "Geral", |
diff --git a/src/i18n/locales/pt.json b/src/i18n/locales/pt.json index 9675d5d77..b0411578b 100644 --- a/src/i18n/locales/pt.json +++ b/src/i18n/locales/pt.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Ativar modo noturno", | 211 | "settings.app.form.darkMode": "Ativar modo noturno", |
212 | "settings.app.form.enableGPUAcceleration": "Ativar Aceleração de GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Ativar Aceleração de GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Ativar palavra-passe", | 214 | "settings.app.form.enableLock": "Ativar palavra-passe", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Ativar correção ortográfica", | 217 | "settings.app.form.enableSpellchecking": "Ativar correção ortográfica", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Ativar notificações", | 312 | "settings.service.form.enableNotification": "Ativar notificações", |
311 | "settings.service.form.enableService": "Ativar serviço", | 313 | "settings.service.form.enableService": "Ativar serviço", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Notificações de Mensagens não lidas", | 315 | "settings.service.form.headlineBadges": "Notificações de Mensagens não lidas", |
313 | "settings.service.form.headlineDarkReaderSettings": "Configurações do Leitor Noturno", | 316 | "settings.service.form.headlineDarkReaderSettings": "Configurações do Leitor Noturno", |
314 | "settings.service.form.headlineGeneral": "Geral", | 317 | "settings.service.form.headlineGeneral": "Geral", |
diff --git a/src/i18n/locales/ro.json b/src/i18n/locales/ro.json index fad6dae82..e6c8f6262 100644 --- a/src/i18n/locales/ro.json +++ b/src/i18n/locales/ro.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/ru.json b/src/i18n/locales/ru.json index f7d25e08b..3d9bd13b1 100644 --- a/src/i18n/locales/ru.json +++ b/src/i18n/locales/ru.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Включить Тёмный режим", | 211 | "settings.app.form.darkMode": "Включить Тёмный режим", |
212 | "settings.app.form.enableGPUAcceleration": "Включить уÑкорение GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Включить уÑкорение GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Включить проверку правопиÑаниÑ", | 217 | "settings.app.form.enableSpellchecking": "Включить проверку правопиÑаниÑ", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Включить уведомлениÑ", | 312 | "settings.service.form.enableNotification": "Включить уведомлениÑ", |
311 | "settings.service.form.enableService": "Включить ÑервиÑ", | 313 | "settings.service.form.enableService": "Включить ÑервиÑ", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Значки непрочитанных Ñообщений", | 315 | "settings.service.form.headlineBadges": "Значки непрочитанных Ñообщений", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Общие", | 317 | "settings.service.form.headlineGeneral": "Общие", |
diff --git a/src/i18n/locales/sk.json b/src/i18n/locales/sk.json index 02d325afd..08fb905cf 100644 --- a/src/i18n/locales/sk.json +++ b/src/i18n/locales/sk.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Zapnúť Dark Mode", | 211 | "settings.app.form.darkMode": "Zapnúť Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Zapnúť GPU zrýchlenie", | 212 | "settings.app.form.enableGPUAcceleration": "Zapnúť GPU zrýchlenie", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Zapnúť kontrolu pravopisu", | 217 | "settings.app.form.enableSpellchecking": "Zapnúť kontrolu pravopisu", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Povoliť oznámenia", | 312 | "settings.service.form.enableNotification": "Povoliť oznámenia", |
311 | "settings.service.form.enableService": "Povoliť službu", | 313 | "settings.service.form.enableService": "Povoliť službu", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Symboly nepreÄÃtaných správ", | 315 | "settings.service.form.headlineBadges": "Symboly nepreÄÃtaných správ", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Všeobecné", | 317 | "settings.service.form.headlineGeneral": "Všeobecné", |
diff --git a/src/i18n/locales/sl.json b/src/i18n/locales/sl.json index 041b02632..ff10978c4 100644 --- a/src/i18n/locales/sl.json +++ b/src/i18n/locales/sl.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/sr.json b/src/i18n/locales/sr.json index 85068ee5c..a058a81ed 100644 --- a/src/i18n/locales/sr.json +++ b/src/i18n/locales/sr.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Омогући убрзање графичке јединице", | 212 | "settings.app.form.enableGPUAcceleration": "Омогући убрзање графичке јединице", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Omogući provjeru pravopisa", | 217 | "settings.app.form.enableSpellchecking": "Omogući provjeru pravopisa", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Omogućite obavijesti", | 312 | "settings.service.form.enableNotification": "Omogućite obavijesti", |
311 | "settings.service.form.enableService": "Omogućite usluge", | 313 | "settings.service.form.enableService": "Omogućite usluge", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Беџеви за непрочитане поруке", | 315 | "settings.service.form.headlineBadges": "Беџеви за непрочитане поруке", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Općenito", | 317 | "settings.service.form.headlineGeneral": "Općenito", |
diff --git a/src/i18n/locales/sv.json b/src/i18n/locales/sv.json index ef254fc5d..c3491f720 100644 --- a/src/i18n/locales/sv.json +++ b/src/i18n/locales/sv.json | |||
@@ -1,6 +1,6 @@ | |||
1 | { | 1 | { |
2 | "app.errorHandler.action": "Ladda om", | 2 | "app.errorHandler.action": "Ladda om", |
3 | "app.errorHandler.headline": "Something went wrong.", | 3 | "app.errorHandler.headline": "NÃ¥gonting gick fel.", |
4 | "changeserver.customServerLabel": "Anpassad server", | 4 | "changeserver.customServerLabel": "Anpassad server", |
5 | "changeserver.headline": "Byt server", | 5 | "changeserver.headline": "Byt server", |
6 | "changeserver.label": "Server", | 6 | "changeserver.label": "Server", |
@@ -9,7 +9,7 @@ | |||
9 | "connectionLostBanner.cta": "Ladda om tjänst", | 9 | "connectionLostBanner.cta": "Ladda om tjänst", |
10 | "connectionLostBanner.informationLink": "Vad hände?", | 10 | "connectionLostBanner.informationLink": "Vad hände?", |
11 | "connectionLostBanner.message": "Åh nej! Ferdi förlorade anslutningen till {name}.", | 11 | "connectionLostBanner.message": "Åh nej! Ferdi förlorade anslutningen till {name}.", |
12 | "feature.basicAuth.signIn": "Sign In", | 12 | "feature.basicAuth.signIn": "Logga in", |
13 | "feature.nightlyBuilds.activate": "Aktivera", | 13 | "feature.nightlyBuilds.activate": "Aktivera", |
14 | "feature.nightlyBuilds.info": "Nattliga kompileringar är väldigt experimentella versioner av Ferdi som kan innehålla ofärdiga och ännu ej testade funktioner. Nattliga kompilering är i första hand tänkt för utvecklare för att kunna testa hur nya funktioner fungerar och presterar. Är du osäker på vad detta innebär rekommenderas du att inte aktivera nattliga kompileringar.", | 14 | "feature.nightlyBuilds.info": "Nattliga kompileringar är väldigt experimentella versioner av Ferdi som kan innehålla ofärdiga och ännu ej testade funktioner. Nattliga kompilering är i första hand tänkt för utvecklare för att kunna testa hur nya funktioner fungerar och presterar. Är du osäker på vad detta innebär rekommenderas du att inte aktivera nattliga kompileringar.", |
15 | "feature.nightlyBuilds.title": "Nattlig kompilering", | 15 | "feature.nightlyBuilds.title": "Nattlig kompilering", |
@@ -23,37 +23,37 @@ | |||
23 | "feature.quickSwitch.info": "Välj en tjänst med TAB, ↑ and ↓. Öppna en tjänst med ENTER.", | 23 | "feature.quickSwitch.info": "Välj en tjänst med TAB, ↑ and ↓. Öppna en tjänst med ENTER.", |
24 | "feature.quickSwitch.search": "Sök...", | 24 | "feature.quickSwitch.search": "Sök...", |
25 | "feature.quickSwitch.title": "Snabbväxling", | 25 | "feature.quickSwitch.title": "Snabbväxling", |
26 | "global.api.unhealthy": "Can't connect to Ferdi online services", | 26 | "global.api.unhealthy": "Kan inte ansluta till Ferdis onlinetjänster", |
27 | "global.cancel": "Cancel", | 27 | "global.cancel": "Avbryt", |
28 | "global.edit": "Redigera", | 28 | "global.edit": "Redigera", |
29 | "global.no": "No", | 29 | "global.no": "Nej", |
30 | "global.notConnectedToTheInternet": "Du är inte ansluten till Internet.", | 30 | "global.notConnectedToTheInternet": "Du är inte ansluten till Internet.", |
31 | "global.ok": "Ok", | 31 | "global.ok": "Ok", |
32 | "global.quit": "Quit", | 32 | "global.quit": "Avsluta", |
33 | "global.quitConfirmation": "Do you really want to quit Ferdi?", | 33 | "global.quitConfirmation": "Vill du verkligen avsluta?", |
34 | "global.save": "Save", | 34 | "global.save": "Spara", |
35 | "global.settings": "Settings", | 35 | "global.settings": "Inställningar", |
36 | "global.spellchecker.useDefault": "Använd systemstandard ({default})", | 36 | "global.spellchecker.useDefault": "Använd systemstandard ({default})", |
37 | "global.spellchecking.autodetect": "Identifiera språk automatiskt", | 37 | "global.spellchecking.autodetect": "Identifiera språk automatiskt", |
38 | "global.spellchecking.autodetect.short": "Automatisk", | 38 | "global.spellchecking.autodetect.short": "Automatisk", |
39 | "global.spellchecking.language": "Rättstavningsspråk", | 39 | "global.spellchecking.language": "Rättstavningsspråk", |
40 | "global.submit": "Submit", | 40 | "global.submit": "Skicka", |
41 | "global.userAgentHelp": "Use 'https://whatmyuseragent.com/' (to discover) or 'https://developers.whatismybrowser.com/useragents/explore/' (to choose) your desired user agent and copy-paste it here.", | 41 | "global.userAgentHelp": "Använd 'https://whatmyuseragent.com/' (för att upptäcka) eller 'https://developers.whatismybrowser.com/useragents/explore/' (för att välja) din önskade användaragent och kopiera-klistra in den här.", |
42 | "global.userAgentPref": "User Agent", | 42 | "global.userAgentPref": "Användaragent", |
43 | "global.yes": "Yes", | 43 | "global.yes": "Ja", |
44 | "import.headline": "Importera dina Ferdi 4-tjänster", | 44 | "import.headline": "Importera dina Ferdi 4-tjänster", |
45 | "import.notSupportedHeadline": "Tjänster som ännu inte stöds i Ferdi 5", | 45 | "import.notSupportedHeadline": "Tjänster som ännu inte stöds i Ferdi 5", |
46 | "import.skip.label": "Jag vill lägga till tjänster manuellt", | 46 | "import.skip.label": "Jag vill lägga till tjänster manuellt", |
47 | "import.submit.label": "Import {count} services", | 47 | "import.submit.label": "Importera {count} tjänster", |
48 | "infobar.authRequestFailed": "Det uppstod fel vid försök då en autentiserad begäran utfördes. Prova att logga ut och in igen om felet kvarstår.", | 48 | "infobar.authRequestFailed": "Det uppstod fel vid försök då en autentiserad begäran utfördes. Prova att logga ut och in igen om felet kvarstår.", |
49 | "infobar.buttonChangelog": "What is new?", | 49 | "infobar.buttonChangelog": "Vad är nytt?", |
50 | "infobar.buttonInstallUpdate": "Starta om & installera uppdatering", | 50 | "infobar.buttonInstallUpdate": "Starta om & installera uppdatering", |
51 | "infobar.buttonReloadServices": "Ladda om tjänster", | 51 | "infobar.buttonReloadServices": "Ladda om tjänster", |
52 | "infobar.hide": "Dölj", | 52 | "infobar.hide": "Dölj", |
53 | "infobar.requiredRequestsFailed": "Kunde inte ladda tjänster och användarinformation", | 53 | "infobar.requiredRequestsFailed": "Kunde inte ladda tjänster och användarinformation", |
54 | "infobar.servicesUpdated": "Dina tjänster har uppdaterats.", | 54 | "infobar.servicesUpdated": "Dina tjänster har uppdaterats.", |
55 | "infobar.updateAvailable": "En ny uppdatering för Ferdi finns tillgänglig.", | 55 | "infobar.updateAvailable": "En ny uppdatering för Ferdi finns tillgänglig.", |
56 | "infobox.dismiss": "Dismiss", | 56 | "infobox.dismiss": "Avfärda", |
57 | "invite.email.label": "E-postadress", | 57 | "invite.email.label": "E-postadress", |
58 | "invite.headline.friends": "Bjud in 3 av dina vänner eller kollegor", | 58 | "invite.headline.friends": "Bjud in 3 av dina vänner eller kollegor", |
59 | "invite.name.label": "Namn", | 59 | "invite.name.label": "Namn", |
@@ -74,7 +74,7 @@ | |||
74 | "login.email.label": "E-postadress", | 74 | "login.email.label": "E-postadress", |
75 | "login.headline": "Logga in", | 75 | "login.headline": "Logga in", |
76 | "login.invalidCredentials": "E-post eller lösenord är felaktigt", | 76 | "login.invalidCredentials": "E-post eller lösenord är felaktigt", |
77 | "login.link.password": "Reset password", | 77 | "login.link.password": "Återställ lösenord", |
78 | "login.link.signup": "Skapa ett gratis konto", | 78 | "login.link.signup": "Skapa ett gratis konto", |
79 | "login.password.label": "Lösenord", | 79 | "login.password.label": "Lösenord", |
80 | "login.serverLogout": "Din session har gått ut. Vänligen logga in på nytt.", | 80 | "login.serverLogout": "Din session har gått ut. Vänligen logga in på nytt.", |
@@ -86,40 +86,40 @@ | |||
86 | "menu.app.autohideMenuBar": "Dölj menyraden automatiskt", | 86 | "menu.app.autohideMenuBar": "Dölj menyraden automatiskt", |
87 | "menu.app.checkForUpdates": "Sök efter uppdateringar", | 87 | "menu.app.checkForUpdates": "Sök efter uppdateringar", |
88 | "menu.app.hide": "Dölj", | 88 | "menu.app.hide": "Dölj", |
89 | "menu.app.hideOthers": "Hide Others", | 89 | "menu.app.hideOthers": "Dölj övriga", |
90 | "menu.app.unhide": "Unhide", | 90 | "menu.app.unhide": "Visa", |
91 | "menu.edit": "Redigera", | 91 | "menu.edit": "Redigera", |
92 | "menu.edit.copy": "Copy", | 92 | "menu.edit.copy": "Kopiera", |
93 | "menu.edit.cut": "Cut", | 93 | "menu.edit.cut": "Klipp ut", |
94 | "menu.edit.delete": "Radera", | 94 | "menu.edit.delete": "Radera", |
95 | "menu.edit.emojiSymbols": "Emojis & symboler", | 95 | "menu.edit.emojiSymbols": "Emojis & symboler", |
96 | "menu.edit.findInPage": "Hitta på sidan", | 96 | "menu.edit.findInPage": "Hitta på sidan", |
97 | "menu.edit.paste": "Paste", | 97 | "menu.edit.paste": "Klistra in", |
98 | "menu.edit.pasteAndMatchStyle": "Paste And Match Style", | 98 | "menu.edit.pasteAndMatchStyle": "Klistra in och matcha stil", |
99 | "menu.edit.redo": "Redo", | 99 | "menu.edit.redo": "Gör om", |
100 | "menu.edit.selectAll": "Select All", | 100 | "menu.edit.selectAll": "Markera allt", |
101 | "menu.edit.speech": "Tal", | 101 | "menu.edit.speech": "Tal", |
102 | "menu.edit.startDictation": "Börja diktera", | 102 | "menu.edit.startDictation": "Börja diktera", |
103 | "menu.edit.startSpeaking": "Börja tala", | 103 | "menu.edit.startSpeaking": "Börja tala", |
104 | "menu.edit.stopSpeaking": "Sluta prata", | 104 | "menu.edit.stopSpeaking": "Sluta prata", |
105 | "menu.edit.undo": "Undo", | 105 | "menu.edit.undo": "Ã…ngra", |
106 | "menu.file": "Fil", | 106 | "menu.file": "Fil", |
107 | "menu.help": "Help", | 107 | "menu.help": "Hjälp", |
108 | "menu.help.changelog": "Ändringslogg", | 108 | "menu.help.changelog": "Ändringslogg", |
109 | "menu.help.debugInfo": "Kopiera felsökningsinformation", | 109 | "menu.help.debugInfo": "Kopiera felsökningsinformation", |
110 | "menu.help.debugInfoCopiedBody": "Din felsökningsinformation har kopierats till ditt urklipp.", | 110 | "menu.help.debugInfoCopiedBody": "Din felsökningsinformation har kopierats till ditt urklipp.", |
111 | "menu.help.debugInfoCopiedHeadline": "Ferdi felsökningsinformation", | 111 | "menu.help.debugInfoCopiedHeadline": "Ferdi felsökningsinformation", |
112 | "menu.help.importExportData": "Import/Export Configuration Data", | 112 | "menu.help.importExportData": "Importera/Exportera konfigurationsdata", |
113 | "menu.help.learnMore": "Läs mer", | 113 | "menu.help.learnMore": "Läs mer", |
114 | "menu.help.privacy": "Integritetspolicy", | 114 | "menu.help.privacy": "Integritetspolicy", |
115 | "menu.help.publishDebugInfo": "Skicka felsökningsinformation", | 115 | "menu.help.publishDebugInfo": "Skicka felsökningsinformation", |
116 | "menu.help.support": "Support", | 116 | "menu.help.support": "Support", |
117 | "menu.help.tos": "Användarvillkor", | 117 | "menu.help.tos": "Användarvillkor", |
118 | "menu.services": "Tjänster", | 118 | "menu.services": "Tjänster", |
119 | "menu.services.activatePreviousService": "Activate previous service", | 119 | "menu.services.activatePreviousService": "Aktivera föregående tjänst", |
120 | "menu.services.addNewService": "Add New Service...", | 120 | "menu.services.addNewService": "Lägg till ny tjänst...", |
121 | "menu.services.goHome": "Hem", | 121 | "menu.services.goHome": "Hem", |
122 | "menu.services.setNextServiceActive": "Activate next service", | 122 | "menu.services.setNextServiceActive": "Aktivera nästa tjänst", |
123 | "menu.todos": "Todos", | 123 | "menu.todos": "Todos", |
124 | "menu.todos.enableTodos": "Aktivera Todos", | 124 | "menu.todos.enableTodos": "Aktivera Todos", |
125 | "menu.view": "Visa", | 125 | "menu.view": "Visa", |
@@ -127,31 +127,31 @@ | |||
127 | "menu.view.forward": "Framåt", | 127 | "menu.view.forward": "Framåt", |
128 | "menu.view.lockFerdi": "LÃ¥s Ferdi", | 128 | "menu.view.lockFerdi": "LÃ¥s Ferdi", |
129 | "menu.view.openQuickSwitch": "Öppna snabbväxling", | 129 | "menu.view.openQuickSwitch": "Öppna snabbväxling", |
130 | "menu.view.reloadFerdi": "Reload Ferdi", | 130 | "menu.view.reloadFerdi": "Ladda om Ferdi", |
131 | "menu.view.reloadService": "Ladda om tjänst", | 131 | "menu.view.reloadService": "Ladda om tjänst", |
132 | "menu.view.reloadTodos": "Ladda om: AttGöra", | 132 | "menu.view.reloadTodos": "Ladda om: AttGöra", |
133 | "menu.view.resetZoom": "Actual Size", | 133 | "menu.view.resetZoom": "Originalstorlek", |
134 | "menu.view.toggleDarkMode": "Växla mörkt läge", | 134 | "menu.view.toggleDarkMode": "Växla mörkt läge", |
135 | "menu.view.toggleDevTools": "Växla utvecklarverktyg", | 135 | "menu.view.toggleDevTools": "Växla utvecklarverktyg", |
136 | "menu.view.toggleFullScreen": "Toggle Full Screen", | 136 | "menu.view.toggleFullScreen": "Växla till/från helskärm", |
137 | "menu.view.toggleServiceDevTools": "Växla tjänsteverktyg för utvecklare", | 137 | "menu.view.toggleServiceDevTools": "Växla tjänsteverktyg för utvecklare", |
138 | "menu.view.toggleTodosDevTools": "Växla Todos utvecklarverktyg", | 138 | "menu.view.toggleTodosDevTools": "Växla Todos utvecklarverktyg", |
139 | "menu.view.zoomIn": "Zoom In", | 139 | "menu.view.zoomIn": "Zooma in", |
140 | "menu.view.zoomOut": "Zoom Out", | 140 | "menu.view.zoomOut": "Zooma ut", |
141 | "menu.window": "Window", | 141 | "menu.window": "Fönster", |
142 | "menu.window.close": "Close", | 142 | "menu.window.close": "Stäng", |
143 | "menu.window.minimize": "Minimize", | 143 | "menu.window.minimize": "Minimera", |
144 | "menu.workspaces": "Arbetsytor", | 144 | "menu.workspaces": "Arbetsytor", |
145 | "menu.workspaces.addNewWorkspace": "Skapa ny arbetsyta...", | 145 | "menu.workspaces.addNewWorkspace": "Skapa ny arbetsyta...", |
146 | "menu.workspaces.closeWorkspaceDrawer": "Stäng arbetsytan", | 146 | "menu.workspaces.closeWorkspaceDrawer": "Stäng arbetsytan", |
147 | "menu.workspaces.defaultWorkspace": "Alla tjänster", | 147 | "menu.workspaces.defaultWorkspace": "Alla tjänster", |
148 | "menu.workspaces.openWorkspaceDrawer": "Öppna arbetsytan", | 148 | "menu.workspaces.openWorkspaceDrawer": "Öppna arbetsytan", |
149 | "password.email.label": "E-postadress", | 149 | "password.email.label": "E-postadress", |
150 | "password.headline": "Reset password", | 150 | "password.headline": "Återställ lösenord", |
151 | "password.link.login": "Logga in på ditt konto", | 151 | "password.link.login": "Logga in på ditt konto", |
152 | "password.link.signup": "Skapa ett gratis konto", | 152 | "password.link.signup": "Skapa ett gratis konto", |
153 | "password.noUser": "No user with that email address was found", | 153 | "password.noUser": "Ingen användare med den e-postadressen hittades", |
154 | "password.successInfo": "Your new password was sent to your email address", | 154 | "password.successInfo": "Ett nytt lösenord har skickats till din e-postadress", |
155 | "service.crashHandler.action": "Ladda om {name}", | 155 | "service.crashHandler.action": "Ladda om {name}", |
156 | "service.crashHandler.autoReload": "Försöker automatiskt återställa {name} om {seconds} sekunder", | 156 | "service.crashHandler.autoReload": "Försöker automatiskt återställa {name} om {seconds} sekunder", |
157 | "service.crashHandler.headline": "Ã…h nej!", | 157 | "service.crashHandler.headline": "Ã…h nej!", |
@@ -166,10 +166,10 @@ | |||
166 | "service.webviewLoader.loading": "Laddar {service}", | 166 | "service.webviewLoader.loading": "Laddar {service}", |
167 | "services.getStarted": "Kom igång", | 167 | "services.getStarted": "Kom igång", |
168 | "services.login": "Logga in för att använda Ferdi.", | 168 | "services.login": "Logga in för att använda Ferdi.", |
169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", | 169 | "services.serverInfo": "Alternativt kan du ändra din Ferdi server genom att klicka på kuggen i det nedre vänstra hörnet. Om du byter över (från en av de hostade servrarna) till att använda Ferdi utan ett konto, informeras om att du kan exportera dina data från den servern och därefter importera den med hjälp av Hjälpmenyn för att återuppliva alla dina arbetsytor och konfigurerade tjänster!", |
170 | "services.serverless": "Använd Ferdi utan ett konto", | 170 | "services.serverless": "Använd Ferdi utan ett konto", |
171 | "services.welcome": "Välkommen till Ferdi", | 171 | "services.welcome": "Välkommen till Ferdi", |
172 | "settings.account.account.editButton": "Edit account", | 172 | "settings.account.account.editButton": "Redigera konto", |
173 | "settings.account.accountUnavailable": "Kontot är inte tillgängligt", | 173 | "settings.account.accountUnavailable": "Kontot är inte tillgängligt", |
174 | "settings.account.accountUnavailableInfo": "Du använder Ferdi utan ett konto. Om du vill använda Ferdi med ett konto och hålla dina tjänster synkroniserade mellan installationer, välj en server i fliken Inställningar och logga in.", | 174 | "settings.account.accountUnavailableInfo": "Du använder Ferdi utan ett konto. Om du vill använda Ferdi med ett konto och hålla dina tjänster synkroniserade mellan installationer, välj en server i fliken Inställningar och logga in.", |
175 | "settings.account.buttonSave": "Uppdatera profil", | 175 | "settings.account.buttonSave": "Uppdatera profil", |
@@ -177,25 +177,25 @@ | |||
177 | "settings.account.deleteEmailSent": "Du har fått ett e-postmeddelande med en länk för att bekräfta raderingen av ditt konto. Ditt konto och data kan inte återställas!", | 177 | "settings.account.deleteEmailSent": "Du har fått ett e-postmeddelande med en länk för att bekräfta raderingen av ditt konto. Ditt konto och data kan inte återställas!", |
178 | "settings.account.deleteInfo": "Om du inte behöver ditt Ferdi-konto längre, kan du ta bort ditt konto och all anknuten information här.", | 178 | "settings.account.deleteInfo": "Om du inte behöver ditt Ferdi-konto längre, kan du ta bort ditt konto och all anknuten information här.", |
179 | "settings.account.headline": "Konto", | 179 | "settings.account.headline": "Konto", |
180 | "settings.account.headlineAccount": "Account information", | 180 | "settings.account.headlineAccount": "Kontoinformation", |
181 | "settings.account.headlineDangerZone": "Danger Zone", | 181 | "settings.account.headlineDangerZone": "Högrisksområde", |
182 | "settings.account.headlineInvoices": "Invoices", | 182 | "settings.account.headlineInvoices": "Fakturor", |
183 | "settings.account.headlinePassword": "Change password", | 183 | "settings.account.headlinePassword": "Ändra lösenord", |
184 | "settings.account.headlineProfile": "Uppdatera profil", | 184 | "settings.account.headlineProfile": "Uppdatera profil", |
185 | "settings.account.successInfo": "Dina ändringar har sparats", | 185 | "settings.account.successInfo": "Dina ändringar har sparats", |
186 | "settings.account.tryReloadServices": "Försök igen", | 186 | "settings.account.tryReloadServices": "Försök igen", |
187 | "settings.account.tryReloadUserInfoRequest": "Försök igen", | 187 | "settings.account.tryReloadUserInfoRequest": "Försök igen", |
188 | "settings.account.userInfoRequestFailed": "Kunde inte ladda användarinformation", | 188 | "settings.account.userInfoRequestFailed": "Kunde inte ladda användarinformation", |
189 | "settings.account.yourLicense": "Your Ferdi License:", | 189 | "settings.account.yourLicense": "Din Ferdi-licens:", |
190 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: {defaultAccentColor})", | 190 | "settings.app.accentColorInfo": "Skriv din accentfärg i ett CSS-kompatibelt format. (Standard: {defaultAccentColor})", |
191 | "settings.app.buttonClearAllCache": "Rensa cache", | 191 | "settings.app.buttonClearAllCache": "Rensa cache", |
192 | "settings.app.buttonInstallUpdate": "Starta om & installera uppdatering", | 192 | "settings.app.buttonInstallUpdate": "Starta om & installera uppdatering", |
193 | "settings.app.buttonOpenFerdiProfileFolder": "Open Profile folder", | 193 | "settings.app.buttonOpenFerdiProfileFolder": "Öppna profilmapp", |
194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Open Service Recipes folder", | 194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Öppna Service Recept mapp", |
195 | "settings.app.buttonSearchForUpdate": "Sök efter uppdateringar", | 195 | "settings.app.buttonSearchForUpdate": "Sök efter uppdateringar", |
196 | "settings.app.cacheInfo": "Ferdis cache använder för närvarande {size} diskutrymme.", | 196 | "settings.app.cacheInfo": "Ferdis cache använder för närvarande {size} diskutrymme.", |
197 | "settings.app.cacheNotCleared": "Kunde inte rensa alla temporära filer", | 197 | "settings.app.cacheNotCleared": "Kunde inte rensa alla temporära filer", |
198 | "settings.app.closeSettings": "Close settings", | 198 | "settings.app.closeSettings": "Stäng inställningarna", |
199 | "settings.app.currentVersion": "Nuvarande version:", | 199 | "settings.app.currentVersion": "Nuvarande version:", |
200 | "settings.app.form.accentColor": "Accentfärg", | 200 | "settings.app.form.accentColor": "Accentfärg", |
201 | "settings.app.form.adaptableDarkMode": "Synkronisera det mörka läget med operativsystemets inställning för mörkt läge", | 201 | "settings.app.form.adaptableDarkMode": "Synkronisera det mörka läget med operativsystemets inställning för mörkt läge", |
@@ -204,18 +204,20 @@ | |||
204 | "settings.app.form.autoLaunchOnStart": "Starta Ferdi vid uppstart", | 204 | "settings.app.form.autoLaunchOnStart": "Starta Ferdi vid uppstart", |
205 | "settings.app.form.automaticUpdates": "Aktivera uppdateringar", | 205 | "settings.app.form.automaticUpdates": "Aktivera uppdateringar", |
206 | "settings.app.form.beta": "Inkludera betaversioner", | 206 | "settings.app.form.beta": "Inkludera betaversioner", |
207 | "settings.app.form.clipboardNotifications": "Don't show notifications for clipboard events", | 207 | "settings.app.form.clipboardNotifications": "Visa inte aviseringar för urklipp händelser", |
208 | "settings.app.form.closeToSystemTray": "Close Ferdi to system tray", | 208 | "settings.app.form.closeToSystemTray": "Stäng Ferdi till systemfältet", |
209 | "settings.app.form.confirmOnQuit": "Confirm when quitting Ferdi", | 209 | "settings.app.form.confirmOnQuit": "Bekräfta när du avslutar Ferdi", |
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Anpassad Todo-server", |
211 | "settings.app.form.darkMode": "Aktivera mörkt läge", | 211 | "settings.app.form.darkMode": "Aktivera mörkt läge", |
212 | "settings.app.form.enableGPUAcceleration": "Aktivera GPU-hårdvaruacceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Aktivera GPU-hårdvaruacceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Aktivera Global genväg för att dölja Ferdi", | ||
213 | "settings.app.form.enableLock": "Aktivera lösenordslås", | 214 | "settings.app.form.enableLock": "Aktivera lösenordslås", |
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 215 | "settings.app.form.enableLongPressServiceHint": "Aktivera servicegenvägledningstips vid långt tryck", |
216 | "settings.app.form.enableMenuBar": "Visa alltid Ferdi i menyraden", | ||
215 | "settings.app.form.enableSpellchecking": "Aktivera stavningskontroll", | 217 | "settings.app.form.enableSpellchecking": "Aktivera stavningskontroll", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Visa alltid ikon i systemfältet", |
217 | "settings.app.form.enableTodos": "Aktivera Ferdi Todos", | 219 | "settings.app.form.enableTodos": "Aktivera Ferdi Todos", |
218 | "settings.app.form.hibernateOnStartup": "Keep services in hibernation on startup", | 220 | "settings.app.form.hibernateOnStartup": "Håll tjänster i viloläge vid start", |
219 | "settings.app.form.hibernationStrategy": "Strategi för vila", | 221 | "settings.app.form.hibernationStrategy": "Strategi för vila", |
220 | "settings.app.form.iconSize": "Ikonstorlek för tjänster", | 222 | "settings.app.form.iconSize": "Ikonstorlek för tjänster", |
221 | "settings.app.form.inactivityLock": "LÃ¥s efter inaktivitet", | 223 | "settings.app.form.inactivityLock": "LÃ¥s efter inaktivitet", |
@@ -224,8 +226,8 @@ | |||
224 | "settings.app.form.lockPassword": "Lösenord", | 226 | "settings.app.form.lockPassword": "Lösenord", |
225 | "settings.app.form.minimizeToSystemTray": "Minimera Ferdi till systemfältet", | 227 | "settings.app.form.minimizeToSystemTray": "Minimera Ferdi till systemfältet", |
226 | "settings.app.form.navigationBarBehaviour": "Navigeringsfältets beteende", | 228 | "settings.app.form.navigationBarBehaviour": "Navigeringsfältets beteende", |
227 | "settings.app.form.notifyTaskBarOnMessage": "Notify TaskBar/Dock on new message", | 229 | "settings.app.form.notifyTaskBarOnMessage": "Meddela Aktivitetsfält/Docka på nytt meddelande", |
228 | "settings.app.form.passwordToggle": "Password toggle", | 230 | "settings.app.form.passwordToggle": "Växla lösenord", |
229 | "settings.app.form.predefinedTodoServer": "Todo-server", | 231 | "settings.app.form.predefinedTodoServer": "Todo-server", |
230 | "settings.app.form.privateNotifications": "Visa inte meddelandeinnehåll i aviseringar", | 232 | "settings.app.form.privateNotifications": "Visa inte meddelandeinnehåll i aviseringar", |
231 | "settings.app.form.reloadAfterResume": "Ladda om Ferdi efter datorn väckts från vila", | 233 | "settings.app.form.reloadAfterResume": "Ladda om Ferdi efter datorn väckts från vila", |
@@ -233,38 +235,38 @@ | |||
233 | "settings.app.form.scheduledDNDEnabled": "Aktivera schemalagt Stör ej-läge", | 235 | "settings.app.form.scheduledDNDEnabled": "Aktivera schemalagt Stör ej-läge", |
234 | "settings.app.form.scheduledDNDEnd": "Till", | 236 | "settings.app.form.scheduledDNDEnd": "Till", |
235 | "settings.app.form.scheduledDNDStart": "Från", | 237 | "settings.app.form.scheduledDNDStart": "Från", |
236 | "settings.app.form.searchEngine": "Search engine", | 238 | "settings.app.form.searchEngine": "Sökmotor", |
237 | "settings.app.form.sentry": "Skicka telemetridata", | 239 | "settings.app.form.sentry": "Skicka telemetridata", |
238 | "settings.app.form.serviceRibbonWidth": "Sidofältets bredd", | 240 | "settings.app.form.serviceRibbonWidth": "Sidofältets bredd", |
239 | "settings.app.form.showDisabledServices": "Visa flikar för inaktiverade tjänster", | 241 | "settings.app.form.showDisabledServices": "Visa flikar för inaktiverade tjänster", |
240 | "settings.app.form.showDragArea": "Visa dragbart område i fönstret", | 242 | "settings.app.form.showDragArea": "Visa dragbart område i fönstret", |
241 | "settings.app.form.showMessagesBadgesWhenMuted": "Visa antal olästa meddelanden när aviseringar är inaktiverade", | 243 | "settings.app.form.showMessagesBadgesWhenMuted": "Visa antal olästa meddelanden när aviseringar är inaktiverade", |
242 | "settings.app.form.splitMode": "Enable Split View Mode", | 244 | "settings.app.form.splitMode": "Aktivera delat visningsläge", |
243 | "settings.app.form.startMinimized": "Starta i minimerat läge", | 245 | "settings.app.form.startMinimized": "Starta i minimerat läge", |
244 | "settings.app.form.universalDarkMode": "Aktivera globalt mörkt läge", | 246 | "settings.app.form.universalDarkMode": "Aktivera globalt mörkt läge", |
245 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", | 247 | "settings.app.form.useTouchIdToUnlock": "Tillåt att TouchID används för att låsa upp Ferdi", |
246 | "settings.app.form.useVerticalStyle": "Use horizontal style", | 248 | "settings.app.form.useVerticalStyle": "Använd horisontell stil", |
247 | "settings.app.form.wakeUpStrategy": "Wake up strategy", | 249 | "settings.app.form.wakeUpStrategy": "Vakna strategi", |
248 | "settings.app.headlineAdvanced": "Avancerat", | 250 | "settings.app.headlineAdvanced": "Avancerat", |
249 | "settings.app.headlineAppearance": "Utseende", | 251 | "settings.app.headlineAppearance": "Utseende", |
250 | "settings.app.headlineGeneral": "Allmänt", | 252 | "settings.app.headlineGeneral": "Allmänt", |
251 | "settings.app.headlineLanguage": "Språk", | 253 | "settings.app.headlineLanguage": "Språk", |
252 | "settings.app.headlinePrivacy": "Privacy", | 254 | "settings.app.headlinePrivacy": "Sekretess", |
253 | "settings.app.headlineUpdates": "Uppdateringar", | 255 | "settings.app.headlineUpdates": "Uppdateringar", |
254 | "settings.app.hibernateInfo": "Som standard kommer Ferdi att hålla alla dina tjänster öppna och laddade i bakgrunden så att de är redo när du vill använda dem. Viloläget kommer att stänga dina tjänster efter ett angivet belopp. Detta är användbart för att spara på arbetsminne eller se till att tjänster inte saktar ner datorn.", | 256 | "settings.app.hibernateInfo": "Som standard kommer Ferdi att hålla alla dina tjänster öppna och laddade i bakgrunden så att de är redo när du vill använda dem. Viloläget kommer att stänga dina tjänster efter ett angivet belopp. Detta är användbart för att spara på arbetsminne eller se till att tjänster inte saktar ner datorn.", |
255 | "settings.app.inactivityLockInfo": "Antal minuter av inaktivitet, varefter Ferdi låses automatiskt. Ange 0 för att inaktivera", | 257 | "settings.app.inactivityLockInfo": "Antal minuter av inaktivitet, varefter Ferdi låses automatiskt. Ange 0 för att inaktivera", |
256 | "settings.app.languageDisclaimer": "Engelska och tyska är officella översättningar. Övriga språk har översatts av gemenskapen.", | 258 | "settings.app.languageDisclaimer": "Engelska och tyska är officella översättningar. Övriga språk har översatts av gemenskapen.", |
257 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", | 259 | "settings.app.lockInfo": "Lösenordslås låter dig skydda dina meddelanden.\nAnvända lösenordsblock, du kommer att bli ombedd att ange ditt lösenord varje gång du startar Ferdi eller låsa Ferdi själv med hjälp av låssymbolen i det nedre vänstra hörnet eller genvägen {lockShortcut}.", |
258 | "settings.app.lockedPassword": "Lösenord", | 260 | "settings.app.lockedPassword": "Lösenord", |
259 | "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdi.", | 261 | "settings.app.lockedPasswordInfo": "Se till att du anger ett lösenord du kommer att komma ihåg.\nOm du tappar bort detta lösenord måste du installera om Ferdi.", |
260 | "settings.app.restartRequired": "Ändringar kräver omstart", | 262 | "settings.app.restartRequired": "Ändringar kräver omstart", |
261 | "settings.app.scheduledDNDInfo": "Schemalagd \"Stör ej\" låter dig definiera en tidsperiod inom vilken du inte vill få meddelanden från Ferdi.", | 263 | "settings.app.scheduledDNDInfo": "Schemalagd \"Stör ej\" låter dig definiera en tidsperiod inom vilken du inte vill få meddelanden från Ferdi.", |
262 | "settings.app.scheduledDNDTimeInfo": "Tid i 24-timmarsformat. Sluttid kan vara före starttid (t.ex. start 17:00, slut 09:00) för att aktivera \"Stör ej\" över natten.", | 264 | "settings.app.scheduledDNDTimeInfo": "Tid i 24-timmarsformat. Sluttid kan vara före starttid (t.ex. start 17:00, slut 09:00) för att aktivera \"Stör ej\" över natten.", |
263 | "settings.app.sentryInfo": "Sending telemetry data allows us to find errors in Ferdi - we will not send any personal information like your message data!", | 265 | "settings.app.sentryInfo": "Genom att skicka telemetri data kan vi hitta fel i Ferdi - vi kommer inte att skicka någon personlig information som dina meddelandedata!", |
264 | "settings.app.spellCheckerLanguageInfo": "Ferdi uses your Mac's build-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", | 266 | "settings.app.spellCheckerLanguageInfo": "Ferdi använder Mac's inbyggda stavningskontroll för att kontrollera skrivfel. Om du vill ändra de språk som stavningskontrollen kontrollerar efter kan du göra det i din Macs Systeminställningar.", |
265 | "settings.app.subheadlineCache": "Cache", | 267 | "settings.app.subheadlineCache": "Cache", |
266 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", | 268 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", |
267 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature.", | 269 | "settings.app.todoServerInfo": "Denna server kommer att användas för \"Ferdi Todo\"-funktionen.", |
268 | "settings.app.translationHelp": "Hjälp oss att översätta Ferdi till ditt språk.", | 270 | "settings.app.translationHelp": "Hjälp oss att översätta Ferdi till ditt språk.", |
269 | "settings.app.universalDarkModeInfo": "Globalt mörkt läge försöker att dynamiskt generera en mörk stil för tjänster som ännu inte stöds.", | 271 | "settings.app.universalDarkModeInfo": "Globalt mörkt läge försöker att dynamiskt generera en mörk stil för tjänster som ännu inte stöds.", |
270 | "settings.app.updateStatusAvailable": "Uppdatering tillgänglig, laddar ner...", | 272 | "settings.app.updateStatusAvailable": "Uppdatering tillgänglig, laddar ner...", |
@@ -283,12 +285,12 @@ | |||
283 | "settings.recipes.customService.headline.communityRecipes": "Tredjepartsrecept från gemenskapen", | 285 | "settings.recipes.customService.headline.communityRecipes": "Tredjepartsrecept från gemenskapen", |
284 | "settings.recipes.customService.headline.customRecipes": "Egna tredjepartsrecept", | 286 | "settings.recipes.customService.headline.customRecipes": "Egna tredjepartsrecept", |
285 | "settings.recipes.customService.headline.devRecipes": "Dina recept för utvecklingsservice", | 287 | "settings.recipes.customService.headline.devRecipes": "Dina recept för utvecklingsservice", |
286 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", | 288 | "settings.recipes.customService.intro": "För att lägga till en anpassad tjänst, kopiera receptet för tjänsten till:", |
287 | "settings.recipes.customService.openDevDocs": "Dokumentation för utvecklare", | 289 | "settings.recipes.customService.openDevDocs": "Dokumentation för utvecklare", |
288 | "settings.recipes.customService.openFolder": "Open folder", | 290 | "settings.recipes.customService.openFolder": "Öppna mapp", |
289 | "settings.recipes.headline": "Tillgängliga tjänster", | 291 | "settings.recipes.headline": "Tillgängliga tjänster", |
290 | "settings.recipes.missingService": "Saknar du en tjänst?", | 292 | "settings.recipes.missingService": "Saknar du en tjänst?", |
291 | "settings.recipes.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 293 | "settings.recipes.nothingFound": "Tyvärr, men ingen tjänst matchade ditt sökord - men du kan fortfarande förmodligen lägga till det med \"Anpassad webbplats\" alternativet. Observera att webbplatsen kan visa fler tjänster som har lagts till Ferdi sedan den version som du för närvarande är på. För att få dessa nya tjänster, överväg att uppgradera till en nyare version av Ferdi.", |
292 | "settings.recipes.servicesSuccessfulAddedInfo": "Tjänsten har lagts till", | 294 | "settings.recipes.servicesSuccessfulAddedInfo": "Tjänsten har lagts till", |
293 | "settings.searchService": "Sök efter tjänst", | 295 | "settings.searchService": "Sök efter tjänst", |
294 | "settings.service.error.goBack": "Tillbaka till tjänster", | 296 | "settings.service.error.goBack": "Tillbaka till tjänster", |
@@ -298,19 +300,20 @@ | |||
298 | "settings.service.form.availableServices": "Tillgängliga tjänster", | 300 | "settings.service.form.availableServices": "Tillgängliga tjänster", |
299 | "settings.service.form.customUrl": "Anpassad server", | 301 | "settings.service.form.customUrl": "Anpassad server", |
300 | "settings.service.form.customUrlValidationError": "Kunde inte validera anpassad {name} -server.", | 302 | "settings.service.form.customUrlValidationError": "Kunde inte validera anpassad {name} -server.", |
301 | "settings.service.form.darkReaderBrightness": "Dark Reader Brightness", | 303 | "settings.service.form.darkReaderBrightness": "Mörk läsare ljusstyrka", |
302 | "settings.service.form.darkReaderContrast": "Dark Reader Contrast", | 304 | "settings.service.form.darkReaderContrast": "Mörk Läsare kontrast", |
303 | "settings.service.form.darkReaderSepia": "Dark Reader Sepia", | 305 | "settings.service.form.darkReaderSepia": "Mörk Läsare Sepia", |
304 | "settings.service.form.deleteButton": "Delete service", | 306 | "settings.service.form.deleteButton": "Ta bort tjänst", |
305 | "settings.service.form.editServiceHeadline": "Redigera {name}", | 307 | "settings.service.form.editServiceHeadline": "Redigera {name}", |
306 | "settings.service.form.enableAudio": "Aktivera ljud", | 308 | "settings.service.form.enableAudio": "Aktivera ljud", |
307 | "settings.service.form.enableBadge": "Visa olästa meddelandemärken", | 309 | "settings.service.form.enableBadge": "Visa olästa meddelandemärken", |
308 | "settings.service.form.enableDarkMode": "Aktivera mörkt läge", | 310 | "settings.service.form.enableDarkMode": "Aktivera mörkt läge", |
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Aktivera hibernation", |
310 | "settings.service.form.enableNotification": "Aktivera aviseringar", | 312 | "settings.service.form.enableNotification": "Aktivera aviseringar", |
311 | "settings.service.form.enableService": "Aktivera tjänst", | 313 | "settings.service.form.enableService": "Aktivera tjänst", |
314 | "settings.service.form.enableWakeUp": "Aktivera vakna", | ||
312 | "settings.service.form.headlineBadges": "Olästa meddelandemärken", | 315 | "settings.service.form.headlineBadges": "Olästa meddelandemärken", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Mörk läsare inställningar", |
314 | "settings.service.form.headlineGeneral": "Allmänt", | 317 | "settings.service.form.headlineGeneral": "Allmänt", |
315 | "settings.service.form.headlineNotifications": "Aviseringar", | 318 | "settings.service.form.headlineNotifications": "Aviseringar", |
316 | "settings.service.form.icon": "Anpassad ikon", | 319 | "settings.service.form.icon": "Anpassad ikon", |
@@ -318,21 +321,21 @@ | |||
318 | "settings.service.form.iconUpload": "Släpp din bild, eller klicka här", | 321 | "settings.service.form.iconUpload": "Släpp din bild, eller klicka här", |
319 | "settings.service.form.indirectMessageInfo": "Du kommer att aviseras om alla nya meddelanden i en kanal, inte bara @username, @channel, @here, ...", | 322 | "settings.service.form.indirectMessageInfo": "Du kommer att aviseras om alla nya meddelanden i en kanal, inte bara @username, @channel, @here, ...", |
320 | "settings.service.form.indirectMessages": "Visa meddelandemärke för alla nya meddelanden", | 323 | "settings.service.form.indirectMessages": "Visa meddelandemärke för alla nya meddelanden", |
321 | "settings.service.form.isHibernatedEnabledInfo": "When enabled, a service will be shut down after a period of time to save system resources.", | 324 | "settings.service.form.isHibernatedEnabledInfo": "När den är aktiverad kommer en tjänst att stängas av efter en tidsperiod för att spara systemresurser.", |
322 | "settings.service.form.isMutedInfo": "Om inaktiverad kommer alla aviseringsljud och ljuduppspelning att tystas", | 325 | "settings.service.form.isMutedInfo": "Om inaktiverad kommer alla aviseringsljud och ljuduppspelning att tystas", |
323 | "settings.service.form.name": "Namn", | 326 | "settings.service.form.name": "Namn", |
324 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", | 327 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Visa endast favoriter i olästa antal", |
325 | "settings.service.form.openDarkmodeCss": "Öppna darkmode.css", | 328 | "settings.service.form.openDarkmodeCss": "Öppna darkmode.css", |
326 | "settings.service.form.openUserCss": "Öppna user.css", | 329 | "settings.service.form.openUserCss": "Öppna user.css", |
327 | "settings.service.form.openUserJs": "Öppna user.js", | 330 | "settings.service.form.openUserJs": "Öppna user.js", |
328 | "settings.service.form.proxy.headline": "Inställningar för HTTP/HTTPS-proxy", | 331 | "settings.service.form.proxy.headline": "Inställningar för HTTP/HTTPS-proxy", |
329 | "settings.service.form.proxy.host": "Proxy-värd/IP", | 332 | "settings.service.form.proxy.host": "Proxy-värd/IP", |
330 | "settings.service.form.proxy.info": "Proxy settings will not be synchronized with the Ferdi servers.", | 333 | "settings.service.form.proxy.info": "Proxyinställningarna kommer inte att synkroniseras med Ferdi servrarna.", |
331 | "settings.service.form.proxy.isEnabled": "Använd proxy", | 334 | "settings.service.form.proxy.isEnabled": "Använd proxy", |
332 | "settings.service.form.proxy.password": "Password (optional)", | 335 | "settings.service.form.proxy.password": "Lösenord (frivilligt)", |
333 | "settings.service.form.proxy.port": "Port", | 336 | "settings.service.form.proxy.port": "Port", |
334 | "settings.service.form.proxy.restartInfo": "Vänligen starta om Ferdi efter att du ändrat proxyinställningar.", | 337 | "settings.service.form.proxy.restartInfo": "Vänligen starta om Ferdi efter att du ändrat proxyinställningar.", |
335 | "settings.service.form.proxy.user": "User (optional)", | 338 | "settings.service.form.proxy.user": "Användare (valfritt)", |
336 | "settings.service.form.recipeFileInfo": "Dina användarfiler kommer att infogas i webbsidan så att du kan anpassa tjänsterna hur du vill. Användarfiler lagras endast lokalt och överförs inte till andra datorer som använer samma konto.", | 339 | "settings.service.form.recipeFileInfo": "Dina användarfiler kommer att infogas i webbsidan så att du kan anpassa tjänsterna hur du vill. Användarfiler lagras endast lokalt och överförs inte till andra datorer som använer samma konto.", |
337 | "settings.service.form.saveButton": "Spara tjänst", | 340 | "settings.service.form.saveButton": "Spara tjänst", |
338 | "settings.service.form.tabHosted": "Värd", | 341 | "settings.service.form.tabHosted": "Värd", |
@@ -343,32 +346,32 @@ | |||
343 | "settings.services.deletedInfo": "Tjänsten har tagits bort", | 346 | "settings.services.deletedInfo": "Tjänsten har tagits bort", |
344 | "settings.services.discoverServices": "Upptäck tjänster", | 347 | "settings.services.discoverServices": "Upptäck tjänster", |
345 | "settings.services.headline": "Dina tjänster", | 348 | "settings.services.headline": "Dina tjänster", |
346 | "settings.services.noServicesAdded": "Start by adding a service.", | 349 | "settings.services.noServicesAdded": "Börja med att lägga till en tjänst.", |
347 | "settings.services.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 350 | "settings.services.nothingFound": "Tyvärr, men ingen tjänst matchade ditt sökord - men du kan fortfarande förmodligen lägga till det med \"Anpassad webbplats\" alternativet. Observera att webbplatsen kan visa fler tjänster som har lagts till Ferdi sedan den version som du för närvarande är på. För att få dessa nya tjänster, överväg att uppgradera till en nyare version av Ferdi.", |
348 | "settings.services.servicesRequestFailed": "Kunde inte ladda dina tjänster", | 351 | "settings.services.servicesRequestFailed": "Kunde inte ladda dina tjänster", |
349 | "settings.services.tooltip.isDisabled": "Tjänsten är inaktiverad", | 352 | "settings.services.tooltip.isDisabled": "Tjänsten är inaktiverad", |
350 | "settings.services.tooltip.isMuted": "Alla ljud är avstängda", | 353 | "settings.services.tooltip.isMuted": "Alla ljud är avstängda", |
351 | "settings.services.tooltip.notificationsDisabled": "Aviseringar är inaktiverade", | 354 | "settings.services.tooltip.notificationsDisabled": "Aviseringar är inaktiverade", |
352 | "settings.services.updatedInfo": "Dina ändringar har sparats", | 355 | "settings.services.updatedInfo": "Dina ändringar har sparats", |
353 | "settings.supportFerdi.aboutIntro": "<p>Ferdi is an open-source and a community-lead application.</p><p>Thanks to the people who make this possbile:</p>", | 356 | "settings.supportFerdi.aboutIntro": "<p>Ferdi är en öppen källkod och en community-ledd applikation.</p><p>Tack till de personer som gör detta besitter:</p>", |
354 | "settings.supportFerdi.bannerText": "Do you want to help us improve Ferdi?", | 357 | "settings.supportFerdi.bannerText": "Vill du hjälpa oss att förbättra Ferdi?", |
355 | "settings.supportFerdi.headline": "Om Ferdi", | 358 | "settings.supportFerdi.headline": "Om Ferdi", |
356 | "settings.supportFerdi.openSurvey": "Open survey", | 359 | "settings.supportFerdi.openSurvey": "Öppna enkäten", |
357 | "settings.supportFerdi.textDonation": "If you feel like supporting Ferdi development with a donation, you can do so on both,", | 360 | "settings.supportFerdi.textDonation": "Om du känner för att stödja Ferdi utveckling med en donation, kan du göra det på båda,", |
358 | "settings.supportFerdi.textDonationAnd": "and", | 361 | "settings.supportFerdi.textDonationAnd": "och", |
359 | "settings.supportFerdi.textExpenses": "While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our", | 362 | "settings.supportFerdi.textExpenses": "Medan volontärer gör det mesta av arbetet, måste vi fortfarande betala för servrar och certifikat. Som ett samhälle är vi helt öppna för medel som vi samlar in och spenderar - se vår", |
360 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", | 363 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", |
361 | "settings.supportFerdi.textListContributors": "Full list of contributors", | 364 | "settings.supportFerdi.textListContributors": "Fullständig lista över bidragsgivare", |
362 | "settings.supportFerdi.textListContributorsHere": "here", | 365 | "settings.supportFerdi.textListContributorsHere": "här", |
363 | "settings.supportFerdi.textOpenCollective": "Open Collective", | 366 | "settings.supportFerdi.textOpenCollective": "Open Collective", |
364 | "settings.supportFerdi.textSupportWelcome": "Support is always welcome. You can find a list of the help we need", | 367 | "settings.supportFerdi.textSupportWelcome": "Stöd är alltid välkommet. Du hittar en lista över den hjälp vi behöver", |
365 | "settings.supportFerdi.textSupportWelcomeHere": "here", | 368 | "settings.supportFerdi.textSupportWelcomeHere": "här", |
366 | "settings.supportFerdi.textVolunteers": "The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.", | 369 | "settings.supportFerdi.textVolunteers": "Utvecklingen av Ferdi görs av volontärer. Människor som använder Ferdi som du. De underhåller, fixar och förbättrar Ferdi på sin fritid.", |
367 | "settings.supportFerdi.title": "Do you like Ferdi?", | 370 | "settings.supportFerdi.title": "Tycker du om Ferdi?", |
368 | "settings.team.contentHeadline": "Hantera Franz-grupp", | 371 | "settings.team.contentHeadline": "Hantera Franz-grupp", |
369 | "settings.team.copy": "Grupphanteringen i Franz låter dig hantera Franz-abonnemang för flera användare. Observera att ett Franz Premium-abonnemang inte ger dig några extra funktioner i Ferdi: Den enda anledningen till att du fortfarande har tillgång till grupphanteringen är att du kan hantera dina äldre Franz-grupper så att du inte förlorar någon funktionalitet i hanteringen av ditt konto.", | 372 | "settings.team.copy": "Grupphanteringen i Franz låter dig hantera Franz-abonnemang för flera användare. Observera att ett Franz Premium-abonnemang inte ger dig några extra funktioner i Ferdi: Den enda anledningen till att du fortfarande har tillgång till grupphanteringen är att du kan hantera dina äldre Franz-grupper så att du inte förlorar någon funktionalitet i hanteringen av ditt konto.", |
370 | "settings.team.headline": "Grupp", | 373 | "settings.team.headline": "Grupp", |
371 | "settings.team.intro": "You are currently using Franz Servers, which is why you have access to Team Management.", | 374 | "settings.team.intro": "Du använder för närvarande Franz servrar, vilket är anledningen till att du har tillgång till Team Management.", |
372 | "settings.team.manageAction": "Hantera din grupp på meetfranz.com", | 375 | "settings.team.manageAction": "Hantera din grupp på meetfranz.com", |
373 | "settings.team.teamsUnavailable": "Grupper är inte tillgängliga", | 376 | "settings.team.teamsUnavailable": "Grupper är inte tillgängliga", |
374 | "settings.team.teamsUnavailableInfo": "Grupper är för närvarande endast tillgängliga när du använder Franz Server och har betalat för Franz Professional. Vänligen ändra din server till https://api.franzinfra.com för att använda gruppfunktionalitet.", | 377 | "settings.team.teamsUnavailableInfo": "Grupper är för närvarande endast tillgängliga när du använder Franz Server och har betalat för Franz Professional. Vänligen ändra din server till https://api.franzinfra.com för att använda gruppfunktionalitet.", |
@@ -378,8 +381,8 @@ | |||
378 | "settings.user.form.accountType.non-profit": "Ideell organisation", | 381 | "settings.user.form.accountType.non-profit": "Ideell organisation", |
379 | "settings.user.form.currentPassword": "Nuvarande lösenord", | 382 | "settings.user.form.currentPassword": "Nuvarande lösenord", |
380 | "settings.user.form.email": "E-post", | 383 | "settings.user.form.email": "E-post", |
381 | "settings.user.form.firstname": "First Name", | 384 | "settings.user.form.firstname": "Förnamn", |
382 | "settings.user.form.lastname": "Last Name", | 385 | "settings.user.form.lastname": "Efternamn", |
383 | "settings.user.form.newPassword": "Nytt lösenord", | 386 | "settings.user.form.newPassword": "Nytt lösenord", |
384 | "settings.workspace.add.form.name": "Namn", | 387 | "settings.workspace.add.form.name": "Namn", |
385 | "settings.workspace.add.form.submitButton": "Skapa arbetsyta", | 388 | "settings.workspace.add.form.submitButton": "Skapa arbetsyta", |
@@ -392,15 +395,15 @@ | |||
392 | "settings.workspace.form.yourWorkspaces": "Dina arbetsytor", | 395 | "settings.workspace.form.yourWorkspaces": "Dina arbetsytor", |
393 | "settings.workspaces.deletedInfo": "Arbetsytan har tagits bort", | 396 | "settings.workspaces.deletedInfo": "Arbetsytan har tagits bort", |
394 | "settings.workspaces.headline": "Dina arbetsytor", | 397 | "settings.workspaces.headline": "Dina arbetsytor", |
395 | "settings.workspaces.noWorkspacesAdded": "You haven't created any workspaces yet.", | 398 | "settings.workspaces.noWorkspacesAdded": "Du har inte lagt till några arbetsytor än.", |
396 | "settings.workspaces.tryReloadWorkspaces": "Försök igen", | 399 | "settings.workspaces.tryReloadWorkspaces": "Försök igen", |
397 | "settings.workspaces.updatedInfo": "Dina ändringar har sparats", | 400 | "settings.workspaces.updatedInfo": "Dina ändringar har sparats", |
398 | "settings.workspaces.workspaceFeatureHeadline": "\"Less is more\": Vi presenterar Ferdi-arbetsytor", | 401 | "settings.workspaces.workspaceFeatureHeadline": "\"Less is more\": Vi presenterar Ferdi-arbetsytor", |
399 | "settings.workspaces.workspaceFeatureInfo": "Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.", | 402 | "settings.workspaces.workspaceFeatureInfo": "Ferdi Workspaces låter dig fokusera på vad som är viktigt just nu. Konfigurera olika uppsättningar av tjänster och växla enkelt mellan dem när som helst. Du bestämmer vilka tjänster du behöver när och var, så att vi kan hjälpa dig att hålla koll på ditt spel - eller enkelt stänga av från jobbet när du vill.", |
400 | "settings.workspaces.workspacesRequestFailed": "Kunde inte ladda dina arbetsytor", | 403 | "settings.workspaces.workspacesRequestFailed": "Kunde inte ladda dina arbetsytor", |
401 | "setupAssistant.headline": "Let's get started", | 404 | "setupAssistant.headline": "Kom igång", |
402 | "setupAssistant.subheadline": "Choose from our most used services and get back on top of your messaging now.", | 405 | "setupAssistant.subheadline": "Välj bland våra mest använda tjänster och kom tillbaka på toppen av dina meddelanden nu.", |
403 | "setupAssistant.submit.label": "Let's go", | 406 | "setupAssistant.submit.label": "Sätt igång", |
404 | "sidebar.addNewService": "Lägg till ny tjänst", | 407 | "sidebar.addNewService": "Lägg till ny tjänst", |
405 | "sidebar.closeTodosDrawer": "Stäng Ferdi Todos", | 408 | "sidebar.closeTodosDrawer": "Stäng Ferdi Todos", |
406 | "sidebar.closeWorkspaceDrawer": "Stäng arbetsytan", | 409 | "sidebar.closeWorkspaceDrawer": "Stäng arbetsytan", |
@@ -411,33 +414,33 @@ | |||
411 | "sidebar.unmuteApp": "Aktivera aviseringar och ljud", | 414 | "sidebar.unmuteApp": "Aktivera aviseringar och ljud", |
412 | "signup.email.label": "E-postadress", | 415 | "signup.email.label": "E-postadress", |
413 | "signup.emailDuplicate": "En användare med den e-postadressen finns redan", | 416 | "signup.emailDuplicate": "En användare med den e-postadressen finns redan", |
414 | "signup.firstname.label": "First Name", | 417 | "signup.firstname.label": "Förnamn", |
415 | "signup.headline": "Registrera dig", | 418 | "signup.headline": "Registrera dig", |
416 | "signup.lastname.label": "Last Name", | 419 | "signup.lastname.label": "Efternamn", |
417 | "signup.legal.info": "Genom att skapa ett Ferdi konto accepterar du", | 420 | "signup.legal.info": "Genom att skapa ett Ferdi konto accepterar du", |
418 | "signup.legal.privacy": "Integritetspolicy", | 421 | "signup.legal.privacy": "Integritetspolicy", |
419 | "signup.legal.terms": "Användarvillkor", | 422 | "signup.legal.terms": "Användarvillkor", |
420 | "signup.link.login": "Har du redan ett konto? Logga in", | 423 | "signup.link.login": "Har du redan ett konto? Logga in", |
421 | "signup.password.label": "Lösenord", | 424 | "signup.password.label": "Lösenord", |
422 | "signup.submit.label": "Skapa konto", | 425 | "signup.submit.label": "Skapa konto", |
423 | "tabs.item.confirmDeleteService": "Do you really want to delete the {serviceName} service?", | 426 | "tabs.item.confirmDeleteService": "Vill du verkligen ta bort tjänsten {serviceName}?", |
424 | "tabs.item.deleteService": "Delete service", | 427 | "tabs.item.deleteService": "Ta bort tjänst", |
425 | "tabs.item.disableAudio": "Inaktivera ljud", | 428 | "tabs.item.disableAudio": "Inaktivera ljud", |
426 | "tabs.item.disableDarkMode": "Disable Dark mode", | 429 | "tabs.item.disableDarkMode": "Inaktivera mörkt läge", |
427 | "tabs.item.disableNotifications": "Inaktivera aviseringar", | 430 | "tabs.item.disableNotifications": "Inaktivera aviseringar", |
428 | "tabs.item.disableService": "Disable service", | 431 | "tabs.item.disableService": "Inaktivera tjänst", |
429 | "tabs.item.enableAudio": "Aktivera ljud", | 432 | "tabs.item.enableAudio": "Aktivera ljud", |
430 | "tabs.item.enableDarkMode": "Enable Dark mode", | 433 | "tabs.item.enableDarkMode": "Aktivera mörkt läge", |
431 | "tabs.item.enableNotification": "Aktivera aviseringar", | 434 | "tabs.item.enableNotification": "Aktivera aviseringar", |
432 | "tabs.item.enableService": "Aktivera tjänst", | 435 | "tabs.item.enableService": "Aktivera tjänst", |
433 | "tabs.item.hibernateService": "Hibernate service", | 436 | "tabs.item.hibernateService": "Hibernate service", |
434 | "tabs.item.reload": "Ladda om", | 437 | "tabs.item.reload": "Ladda om", |
435 | "tabs.item.wakeUpService": "Wake up service", | 438 | "tabs.item.wakeUpService": "Väck enhet", |
436 | "validation.email": "{field} is not valid", | 439 | "validation.email": "{field} är felaktig", |
437 | "validation.minLength": "{field} should be at least {length} characters long", | 440 | "validation.minLength": "{field} bör vara minst {length} tecken lång", |
438 | "validation.oneRequired": "Minst en krävs", | 441 | "validation.oneRequired": "Minst en krävs", |
439 | "validation.required": "{field} is required", | 442 | "validation.required": "{field} krävs", |
440 | "validation.url": "{field} is not a valid URL", | 443 | "validation.url": "{field} är inte en giltig URL", |
441 | "webControls.back": "Tillbaka", | 444 | "webControls.back": "Tillbaka", |
442 | "webControls.forward": "Framåt", | 445 | "webControls.forward": "Framåt", |
443 | "webControls.goHome": "Hem", | 446 | "webControls.goHome": "Hem", |
@@ -445,12 +448,12 @@ | |||
445 | "webControls.reload": "Ladda om", | 448 | "webControls.reload": "Ladda om", |
446 | "welcome.loginButton": "Logga in på ditt konto", | 449 | "welcome.loginButton": "Logga in på ditt konto", |
447 | "welcome.signupButton": "Skapa ett gratis konto", | 450 | "welcome.signupButton": "Skapa ett gratis konto", |
448 | "workspaceDrawer.addNewWorkspaceLabel": "Add new workspace", | 451 | "workspaceDrawer.addNewWorkspaceLabel": "Skapa ny arbetsyta", |
449 | "workspaceDrawer.allServices": "Alla tjänster", | 452 | "workspaceDrawer.allServices": "Alla tjänster", |
450 | "workspaceDrawer.headline": "Arbetsytor", | 453 | "workspaceDrawer.headline": "Arbetsytor", |
451 | "workspaceDrawer.item.contextMenuEdit": "redigera", | 454 | "workspaceDrawer.item.contextMenuEdit": "redigera", |
452 | "workspaceDrawer.item.noServicesAddedYet": "Inga tjänster har lagts till", | 455 | "workspaceDrawer.item.noServicesAddedYet": "Inga tjänster har lagts till", |
453 | "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", | 456 | "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi-arbetsytor låter dig fokusera på det som är viktigt just nu. Konfigurera olika uppsättningar av tjänster och växla enkelt mellan dem när som helst.</p><p>Du bestämmer vilka tjänster du behöver när och var, så att vi kan hjälpa dig att hålla koll på läget - eller enkelt koppla bort från jobbet när du vill.</p>", |
454 | "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", | 457 | "workspaceDrawer.workspacesSettingsTooltip": "Redigera inställningar för arbetsytor", |
455 | "workspaces.switchingIndicator.switchingTo": "Byter till" | 458 | "workspaces.switchingIndicator.switchingTo": "Byter till" |
456 | } | 459 | } |
diff --git a/src/i18n/locales/te.json b/src/i18n/locales/te.json index 812ec8c4c..d5cf072f6 100644 --- a/src/i18n/locales/te.json +++ b/src/i18n/locales/te.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "Enable Dark Mode", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "Enable notifications", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "Enable service", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "General", |
diff --git a/src/i18n/locales/tr.json b/src/i18n/locales/tr.json index b0368a18a..99e4d4358 100644 --- a/src/i18n/locales/tr.json +++ b/src/i18n/locales/tr.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Karanlık modu aç", | 211 | "settings.app.form.darkMode": "Karanlık modu aç", |
212 | "settings.app.form.enableGPUAcceleration": "Grafik İşlemci Ünitesi (GPU) Hızlandırıcısını Aktif et", | 212 | "settings.app.form.enableGPUAcceleration": "Grafik İşlemci Ünitesi (GPU) Hızlandırıcısını Aktif et", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Yazım denetimini etkinleştir", | 217 | "settings.app.form.enableSpellchecking": "Yazım denetimini etkinleştir", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Bildirimleri etkinleÅŸtir", | 312 | "settings.service.form.enableNotification": "Bildirimleri etkinleÅŸtir", |
311 | "settings.service.form.enableService": "Servisi etkinleÅŸtir", | 313 | "settings.service.form.enableService": "Servisi etkinleÅŸtir", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Okunmamış mesajlar", | 315 | "settings.service.form.headlineBadges": "Okunmamış mesajlar", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Genel", | 317 | "settings.service.form.headlineGeneral": "Genel", |
diff --git a/src/i18n/locales/uk.json b/src/i18n/locales/uk.json index b03bf9072..d1cf6f9c7 100644 --- a/src/i18n/locales/uk.json +++ b/src/i18n/locales/uk.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Переходь на Темну Сторону", | 211 | "settings.app.form.darkMode": "Переходь на Темну Сторону", |
212 | "settings.app.form.enableGPUAcceleration": "Ввімкнути приÑÐºÐ¾Ñ€ÐµÐ½Ð½Ñ GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Ввімкнути приÑÐºÐ¾Ñ€ÐµÐ½Ð½Ñ GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Увімкнути перевірку орфографії", | 217 | "settings.app.form.enableSpellchecking": "Увімкнути перевірку орфографії", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "Enable hibernation", |
310 | "settings.service.form.enableNotification": "Увімкнути ÑповіщеннÑ", | 312 | "settings.service.form.enableNotification": "Увімкнути ÑповіщеннÑ", |
311 | "settings.service.form.enableService": "Увімкнути ÑервіÑ", | 313 | "settings.service.form.enableService": "Увімкнути ÑервіÑ", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Значки непрочитаних повідомлень", | 315 | "settings.service.form.headlineBadges": "Значки непрочитаних повідомлень", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "Загальні", | 317 | "settings.service.form.headlineGeneral": "Загальні", |
diff --git a/src/i18n/locales/vi.json b/src/i18n/locales/vi.json index 668b75127..e658b959c 100644 --- a/src/i18n/locales/vi.json +++ b/src/i18n/locales/vi.json | |||
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "Cho phép chế Ä‘á»™ ná»n tối", | 211 | "settings.app.form.darkMode": "Cho phép chế Ä‘á»™ ná»n tối", |
212 | "settings.app.form.enableGPUAcceleration": "Báºt Tăng tốc GPU", | 212 | "settings.app.form.enableGPUAcceleration": "Báºt Tăng tốc GPU", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Cho phép khóa bằng máºt khẩu", | 214 | "settings.app.form.enableLock": "Cho phép khóa bằng máºt khẩu", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Luôn hiển thị Ferdi trong Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Luôn hiển thị Ferdi trong Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "KÃch hoạt tÃnh năng kiểm tra chÃnh tả", | 217 | "settings.app.form.enableSpellchecking": "KÃch hoạt tÃnh năng kiểm tra chÃnh tả", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -309,6 +311,7 @@ | |||
309 | "settings.service.form.enableHibernation": "KÃch hoạt ngủ đông", | 311 | "settings.service.form.enableHibernation": "KÃch hoạt ngủ đông", |
310 | "settings.service.form.enableNotification": "KÃch hoạt thông báo", | 312 | "settings.service.form.enableNotification": "KÃch hoạt thông báo", |
311 | "settings.service.form.enableService": "KÃch hoạt dịch vụ", | 313 | "settings.service.form.enableService": "KÃch hoạt dịch vụ", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Huy hiệu tin nhắn chÆ°a Ä‘á»c", | 315 | "settings.service.form.headlineBadges": "Huy hiệu tin nhắn chÆ°a Ä‘á»c", |
313 | "settings.service.form.headlineDarkReaderSettings": "Cà i đặt trình Ä‘á»c tối", | 316 | "settings.service.form.headlineDarkReaderSettings": "Cà i đặt trình Ä‘á»c tối", |
314 | "settings.service.form.headlineGeneral": "Chung", | 317 | "settings.service.form.headlineGeneral": "Chung", |
diff --git a/src/i18n/locales/zh-HANT.json b/src/i18n/locales/zh-HANT.json index ec6d9085b..30c81436d 100644 --- a/src/i18n/locales/zh-HANT.json +++ b/src/i18n/locales/zh-HANT.json | |||
@@ -9,8 +9,8 @@ | |||
9 | "connectionLostBanner.cta": "é‡æ–°è¼‰å…¥", | 9 | "connectionLostBanner.cta": "é‡æ–°è¼‰å…¥", |
10 | "connectionLostBanner.informationLink": "What happened?", | 10 | "connectionLostBanner.informationLink": "What happened?", |
11 | "connectionLostBanner.message": "Oh no! Ferdi lost the connection to {name}.", | 11 | "connectionLostBanner.message": "Oh no! Ferdi lost the connection to {name}.", |
12 | "feature.basicAuth.signIn": "Sign In", | 12 | "feature.basicAuth.signIn": "登入", |
13 | "feature.nightlyBuilds.activate": "Activate", | 13 | "feature.nightlyBuilds.activate": "å•Ÿå‹•", |
14 | "feature.nightlyBuilds.info": "Nightly builds are highly experimental versions of Ferdi that may contain unpolished or uncompleted features. These nightly builds are mainly used by developers to test their newly developed features and how they will perform in the final build. If you don't know what you are doing, we suggest not activating nightly builds.", | 14 | "feature.nightlyBuilds.info": "Nightly builds are highly experimental versions of Ferdi that may contain unpolished or uncompleted features. These nightly builds are mainly used by developers to test their newly developed features and how they will perform in the final build. If you don't know what you are doing, we suggest not activating nightly builds.", |
15 | "feature.nightlyBuilds.title": "Nightly Builds", | 15 | "feature.nightlyBuilds.title": "Nightly Builds", |
16 | "feature.publishDebugInfo.error": "There was an error while trying to publish the debug information. Please try again later or view the console for more information.", | 16 | "feature.publishDebugInfo.error": "There was an error while trying to publish the debug information. Please try again later or view the console for more information.", |
@@ -22,25 +22,25 @@ | |||
22 | "feature.publishDebugInfo.title": "Publish debug information", | 22 | "feature.publishDebugInfo.title": "Publish debug information", |
23 | "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.", | 23 | "feature.quickSwitch.info": "Select a service with TAB, ↑ and ↓. Open a service with ENTER.", |
24 | "feature.quickSwitch.search": "æœå°‹...", | 24 | "feature.quickSwitch.search": "æœå°‹...", |
25 | "feature.quickSwitch.title": "QuickSwitch", | 25 | "feature.quickSwitch.title": "快速切æ›", |
26 | "global.api.unhealthy": "Can't connect to Ferdi online services", | 26 | "global.api.unhealthy": "Can't connect to Ferdi online services", |
27 | "global.cancel": "Cancel", | 27 | "global.cancel": "å–消", |
28 | "global.edit": "編輯", | 28 | "global.edit": "編輯", |
29 | "global.no": "No", | 29 | "global.no": "å¦", |
30 | "global.notConnectedToTheInternet": "您未連上網際網路", | 30 | "global.notConnectedToTheInternet": "您未連上網際網路", |
31 | "global.ok": "Ok", | 31 | "global.ok": "好", |
32 | "global.quit": "Quit", | 32 | "global.quit": "退出", |
33 | "global.quitConfirmation": "Do you really want to quit Ferdi?", | 33 | "global.quitConfirmation": "Do you really want to quit Ferdi?", |
34 | "global.save": "Save", | 34 | "global.save": "儲å˜", |
35 | "global.settings": "Settings", | 35 | "global.settings": "è¨å®š", |
36 | "global.spellchecker.useDefault": "使用系統é è¨å€¼({default})", | 36 | "global.spellchecker.useDefault": "使用系統é è¨å€¼({default})", |
37 | "global.spellchecking.autodetect": "自動檢測語言", | 37 | "global.spellchecking.autodetect": "自動檢測語言", |
38 | "global.spellchecking.autodetect.short": "自動", | 38 | "global.spellchecking.autodetect.short": "自動", |
39 | "global.spellchecking.language": "拼å—檢查語言", | 39 | "global.spellchecking.language": "拼å—檢查語言", |
40 | "global.submit": "Submit", | 40 | "global.submit": "é€å‡º", |
41 | "global.userAgentHelp": "Use 'https://whatmyuseragent.com/' (to discover) or 'https://developers.whatismybrowser.com/useragents/explore/' (to choose) your desired user agent and copy-paste it here.", | 41 | "global.userAgentHelp": "Use 'https://whatmyuseragent.com/' (to discover) or 'https://developers.whatismybrowser.com/useragents/explore/' (to choose) your desired user agent and copy-paste it here.", |
42 | "global.userAgentPref": "User Agent", | 42 | "global.userAgentPref": "User Agent", |
43 | "global.yes": "Yes", | 43 | "global.yes": "是", |
44 | "import.headline": "匯入您的 Ferdi 4 æœå‹™", | 44 | "import.headline": "匯入您的 Ferdi 4 æœå‹™", |
45 | "import.notSupportedHeadline": "æ¤æœå‹™ä¸è¢« Ferdi 5 支æŒ", | 45 | "import.notSupportedHeadline": "æ¤æœå‹™ä¸è¢« Ferdi 5 支æŒ", |
46 | "import.skip.label": "我想手動匯入", | 46 | "import.skip.label": "我想手動匯入", |
@@ -56,7 +56,7 @@ | |||
56 | "infobox.dismiss": "Dismiss", | 56 | "infobox.dismiss": "Dismiss", |
57 | "invite.email.label": "é›»å郵件信箱", | 57 | "invite.email.label": "é›»å郵件信箱", |
58 | "invite.headline.friends": "邀請三個人", | 58 | "invite.headline.friends": "邀請三個人", |
59 | "invite.name.label": "åå", | 59 | "invite.name.label": "åå—", |
60 | "invite.skip.label": "我想晚點進行", | 60 | "invite.skip.label": "我想晚點進行", |
61 | "invite.submit.label": "發é€é‚€è«‹", | 61 | "invite.submit.label": "發é€é‚€è«‹", |
62 | "invite.successInfo": "å·²æˆåŠŸç™¼é€é‚€è«‹", | 62 | "invite.successInfo": "å·²æˆåŠŸç™¼é€é‚€è«‹", |
@@ -89,20 +89,20 @@ | |||
89 | "menu.app.hideOthers": "Hide Others", | 89 | "menu.app.hideOthers": "Hide Others", |
90 | "menu.app.unhide": "Unhide", | 90 | "menu.app.unhide": "Unhide", |
91 | "menu.edit": "編輯", | 91 | "menu.edit": "編輯", |
92 | "menu.edit.copy": "Copy", | 92 | "menu.edit.copy": "複製", |
93 | "menu.edit.cut": "Cut", | 93 | "menu.edit.cut": "剪下", |
94 | "menu.edit.delete": "刪除", | 94 | "menu.edit.delete": "刪除", |
95 | "menu.edit.emojiSymbols": "Emoji & Symbols", | 95 | "menu.edit.emojiSymbols": "Emoji & Symbols", |
96 | "menu.edit.findInPage": "Find in Page", | 96 | "menu.edit.findInPage": "在é é¢ä¸å°‹æ‰¾", |
97 | "menu.edit.paste": "Paste", | 97 | "menu.edit.paste": "貼上", |
98 | "menu.edit.pasteAndMatchStyle": "Paste And Match Style", | 98 | "menu.edit.pasteAndMatchStyle": "Paste And Match Style", |
99 | "menu.edit.redo": "Redo", | 99 | "menu.edit.redo": "Redo", |
100 | "menu.edit.selectAll": "Select All", | 100 | "menu.edit.selectAll": "å…¨é¸", |
101 | "menu.edit.speech": "語音", | 101 | "menu.edit.speech": "語音", |
102 | "menu.edit.startDictation": "開始è½å¯«", | 102 | "menu.edit.startDictation": "開始è½å¯«", |
103 | "menu.edit.startSpeaking": "開始朗讀", | 103 | "menu.edit.startSpeaking": "開始朗讀", |
104 | "menu.edit.stopSpeaking": "åœæ¢æœ—讀", | 104 | "menu.edit.stopSpeaking": "åœæ¢æœ—讀", |
105 | "menu.edit.undo": "Undo", | 105 | "menu.edit.undo": "復原", |
106 | "menu.file": "檔案", | 106 | "menu.file": "檔案", |
107 | "menu.help": "Help", | 107 | "menu.help": "Help", |
108 | "menu.help.changelog": "更新日誌", | 108 | "menu.help.changelog": "更新日誌", |
@@ -127,20 +127,20 @@ | |||
127 | "menu.view.forward": "å‰é€²", | 127 | "menu.view.forward": "å‰é€²", |
128 | "menu.view.lockFerdi": "Lock Ferdi", | 128 | "menu.view.lockFerdi": "Lock Ferdi", |
129 | "menu.view.openQuickSwitch": "Open Quick Switch", | 129 | "menu.view.openQuickSwitch": "Open Quick Switch", |
130 | "menu.view.reloadFerdi": "Reload Ferdi", | 130 | "menu.view.reloadFerdi": "é‡æ–°è¼‰å…¥ Ferdi", |
131 | "menu.view.reloadService": "é‡æ–°è¼‰å…¥", | 131 | "menu.view.reloadService": "é‡æ–°è¼‰å…¥", |
132 | "menu.view.reloadTodos": "Reload ToDos", | 132 | "menu.view.reloadTodos": "Reload ToDos", |
133 | "menu.view.resetZoom": "Actual Size", | 133 | "menu.view.resetZoom": "實際大å°", |
134 | "menu.view.toggleDarkMode": "Toggle Dark Mode", | 134 | "menu.view.toggleDarkMode": "Toggle Dark Mode", |
135 | "menu.view.toggleDevTools": "切æ›é–‹ç™¼äººå“¡å·¥å…·", | 135 | "menu.view.toggleDevTools": "切æ›é–‹ç™¼äººå“¡å·¥å…·", |
136 | "menu.view.toggleFullScreen": "Toggle Full Screen", | 136 | "menu.view.toggleFullScreen": "Toggle Full Screen", |
137 | "menu.view.toggleServiceDevTools": "切æ›é–‹ç™¼äººå“¡æœå‹™å·¥å…·", | 137 | "menu.view.toggleServiceDevTools": "切æ›é–‹ç™¼äººå“¡æœå‹™å·¥å…·", |
138 | "menu.view.toggleTodosDevTools": "Toggle Todos Developer Tools", | 138 | "menu.view.toggleTodosDevTools": "Toggle Todos Developer Tools", |
139 | "menu.view.zoomIn": "Zoom In", | 139 | "menu.view.zoomIn": "放大", |
140 | "menu.view.zoomOut": "Zoom Out", | 140 | "menu.view.zoomOut": "縮å°", |
141 | "menu.window": "Window", | 141 | "menu.window": "視窗", |
142 | "menu.window.close": "Close", | 142 | "menu.window.close": "關閉", |
143 | "menu.window.minimize": "Minimize", | 143 | "menu.window.minimize": "最å°åŒ–", |
144 | "menu.workspaces": "工作å€", | 144 | "menu.workspaces": "工作å€", |
145 | "menu.workspaces.addNewWorkspace": "新增工作å€â€¦", | 145 | "menu.workspaces.addNewWorkspace": "新增工作å€â€¦", |
146 | "menu.workspaces.closeWorkspaceDrawer": "關閉工作å€", | 146 | "menu.workspaces.closeWorkspaceDrawer": "關閉工作å€", |
@@ -152,13 +152,13 @@ | |||
152 | "password.link.signup": "建立一個å…費帳戶", | 152 | "password.link.signup": "建立一個å…費帳戶", |
153 | "password.noUser": "No user with that email address was found", | 153 | "password.noUser": "No user with that email address was found", |
154 | "password.successInfo": "Your new password was sent to your email address", | 154 | "password.successInfo": "Your new password was sent to your email address", |
155 | "service.crashHandler.action": "Reload {name}", | 155 | "service.crashHandler.action": "é‡æ–°è¼‰å…¥ {name}", |
156 | "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds", | 156 | "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds", |
157 | "service.crashHandler.headline": "唉呀 糟糕!", | 157 | "service.crashHandler.headline": "唉呀 糟糕!", |
158 | "service.crashHandler.text": "{name} é€ æˆäº†å•é¡Œ", | 158 | "service.crashHandler.text": "{name} é€ æˆäº†å•é¡Œ", |
159 | "service.disabledHandler.action": "啟用 {name}", | 159 | "service.disabledHandler.action": "啟用 {name}", |
160 | "service.disabledHandler.headline": "{name} å·²åœç”¨", | 160 | "service.disabledHandler.headline": "{name} å·²åœç”¨", |
161 | "service.errorHandler.action": "Reload {name}", | 161 | "service.errorHandler.action": "é‡æ–°è¼‰å…¥ {name}", |
162 | "service.errorHandler.editAction": "編輯 {name}", | 162 | "service.errorHandler.editAction": "編輯 {name}", |
163 | "service.errorHandler.headline": "唉呀 糟糕!", | 163 | "service.errorHandler.headline": "唉呀 糟糕!", |
164 | "service.errorHandler.message": "錯誤", | 164 | "service.errorHandler.message": "錯誤", |
@@ -169,7 +169,7 @@ | |||
169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", | 169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", |
170 | "services.serverless": "Use Ferdi without an Account", | 170 | "services.serverless": "Use Ferdi without an Account", |
171 | "services.welcome": "æ¡è¿Žä½¿ç”¨ Ferdi", | 171 | "services.welcome": "æ¡è¿Žä½¿ç”¨ Ferdi", |
172 | "settings.account.account.editButton": "Edit account", | 172 | "settings.account.account.editButton": "更改帳戶資訊", |
173 | "settings.account.accountUnavailable": "帳號無法使用", | 173 | "settings.account.accountUnavailable": "帳號無法使用", |
174 | "settings.account.accountUnavailableInfo": "You are using Ferdi without an account. If you want to use Ferdi with an account and keep your services synchronized across installations, please select a server in the Settings tab then login.", | 174 | "settings.account.accountUnavailableInfo": "You are using Ferdi without an account. If you want to use Ferdi with an account and keep your services synchronized across installations, please select a server in the Settings tab then login.", |
175 | "settings.account.buttonSave": "更新帳戶資訊", | 175 | "settings.account.buttonSave": "更新帳戶資訊", |
@@ -177,10 +177,10 @@ | |||
177 | "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", | 177 | "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", |
178 | "settings.account.deleteInfo": "If you don't need your Ferdi account any longer, you can delete your account and all related data here.", | 178 | "settings.account.deleteInfo": "If you don't need your Ferdi account any longer, you can delete your account and all related data here.", |
179 | "settings.account.headline": "帳戶", | 179 | "settings.account.headline": "帳戶", |
180 | "settings.account.headlineAccount": "Account information", | 180 | "settings.account.headlineAccount": "帳戶資訊", |
181 | "settings.account.headlineDangerZone": "Danger Zone", | 181 | "settings.account.headlineDangerZone": "Danger Zone", |
182 | "settings.account.headlineInvoices": "Invoices", | 182 | "settings.account.headlineInvoices": "Invoices", |
183 | "settings.account.headlinePassword": "Change password", | 183 | "settings.account.headlinePassword": "更改密碼", |
184 | "settings.account.headlineProfile": "更新帳戶資訊", | 184 | "settings.account.headlineProfile": "更新帳戶資訊", |
185 | "settings.account.successInfo": "您的更改已經儲å˜", | 185 | "settings.account.successInfo": "您的更改已經儲å˜", |
186 | "settings.account.tryReloadServices": "å†è©¦ä¸€æ¬¡", | 186 | "settings.account.tryReloadServices": "å†è©¦ä¸€æ¬¡", |
@@ -195,14 +195,14 @@ | |||
195 | "settings.app.buttonSearchForUpdate": "檢查更新", | 195 | "settings.app.buttonSearchForUpdate": "檢查更新", |
196 | "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", | 196 | "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", |
197 | "settings.app.cacheNotCleared": "Couldn't clear all cache", | 197 | "settings.app.cacheNotCleared": "Couldn't clear all cache", |
198 | "settings.app.closeSettings": "Close settings", | 198 | "settings.app.closeSettings": "關閉è¨å®š", |
199 | "settings.app.currentVersion": "當å‰ç‰ˆæœ¬ï¼š", | 199 | "settings.app.currentVersion": "當å‰ç‰ˆæœ¬ï¼š", |
200 | "settings.app.form.accentColor": "強調é¡è‰²", | 200 | "settings.app.form.accentColor": "強調é¡è‰²", |
201 | "settings.app.form.adaptableDarkMode": "Synchronize dark mode with my OS's dark mode setting", | 201 | "settings.app.form.adaptableDarkMode": "Synchronize dark mode with my OS's dark mode setting", |
202 | "settings.app.form.alwaysShowWorkspaces": "Always show workspace drawer", | 202 | "settings.app.form.alwaysShowWorkspaces": "Always show workspace drawer", |
203 | "settings.app.form.autoLaunchInBackground": "背景啟動", | 203 | "settings.app.form.autoLaunchInBackground": "背景啟動", |
204 | "settings.app.form.autoLaunchOnStart": "開機時啟動", | 204 | "settings.app.form.autoLaunchOnStart": "開機時啟動", |
205 | "settings.app.form.automaticUpdates": "Enable updates", | 205 | "settings.app.form.automaticUpdates": "啟用更新", |
206 | "settings.app.form.beta": "包å«é–‹ç™¼ä¸ç‰ˆæœ¬", | 206 | "settings.app.form.beta": "包å«é–‹ç™¼ä¸ç‰ˆæœ¬", |
207 | "settings.app.form.clipboardNotifications": "Don't show notifications for clipboard events", | 207 | "settings.app.form.clipboardNotifications": "Don't show notifications for clipboard events", |
208 | "settings.app.form.closeToSystemTray": "Close Ferdi to system tray", | 208 | "settings.app.form.closeToSystemTray": "Close Ferdi to system tray", |
@@ -210,7 +210,9 @@ | |||
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "Custom Todo Server", |
211 | "settings.app.form.darkMode": "啟用夜間模å¼", | 211 | "settings.app.form.darkMode": "啟用夜間模å¼", |
212 | "settings.app.form.enableGPUAcceleration": "開啟顯示å¡ï¼ˆGPUï¼‰åŠ é€Ÿ", | 212 | "settings.app.form.enableGPUAcceleration": "開啟顯示å¡ï¼ˆGPUï¼‰åŠ é€Ÿ", |
213 | "settings.app.form.enableGlobalHideShortcut": "Enable Global shortcut to hide Ferdi", | ||
213 | "settings.app.form.enableLock": "Enable Password Lock", | 214 | "settings.app.form.enableLock": "Enable Password Lock", |
215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", | ||
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 216 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 217 | "settings.app.form.enableSpellchecking": "Enable spell checking", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 218 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", |
@@ -233,9 +235,9 @@ | |||
233 | "settings.app.form.scheduledDNDEnabled": "Enable scheduled Do-not-Disturb", | 235 | "settings.app.form.scheduledDNDEnabled": "Enable scheduled Do-not-Disturb", |
234 | "settings.app.form.scheduledDNDEnd": "至", | 236 | "settings.app.form.scheduledDNDEnd": "至", |
235 | "settings.app.form.scheduledDNDStart": "從", | 237 | "settings.app.form.scheduledDNDStart": "從", |
236 | "settings.app.form.searchEngine": "Search engine", | 238 | "settings.app.form.searchEngine": "æœå°‹å¼•æ“Ž", |
237 | "settings.app.form.sentry": "Send telemetry data", | 239 | "settings.app.form.sentry": "Send telemetry data", |
238 | "settings.app.form.serviceRibbonWidth": "Sidebar width", | 240 | "settings.app.form.serviceRibbonWidth": "å´é‚Šæ¬„寬度", |
239 | "settings.app.form.showDisabledServices": "Display disabled services tabs", | 241 | "settings.app.form.showDisabledServices": "Display disabled services tabs", |
240 | "settings.app.form.showDragArea": "Show draggable area on window", | 242 | "settings.app.form.showDragArea": "Show draggable area on window", |
241 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", | 243 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", |
@@ -249,7 +251,7 @@ | |||
249 | "settings.app.headlineAppearance": "外觀", | 251 | "settings.app.headlineAppearance": "外觀", |
250 | "settings.app.headlineGeneral": "一般", | 252 | "settings.app.headlineGeneral": "一般", |
251 | "settings.app.headlineLanguage": "語言", | 253 | "settings.app.headlineLanguage": "語言", |
252 | "settings.app.headlinePrivacy": "Privacy", | 254 | "settings.app.headlinePrivacy": "éš±ç§", |
253 | "settings.app.headlineUpdates": "æ›´æ–°", | 255 | "settings.app.headlineUpdates": "æ›´æ–°", |
254 | "settings.app.hibernateInfo": "By default, Ferdi will keep all your services open and loaded in the background so they are ready when you want to use them. Service Hibernation will unload your services after a specified amount. This is useful to save RAM or keeping services from slowing down your computer.", | 256 | "settings.app.hibernateInfo": "By default, Ferdi will keep all your services open and loaded in the background so they are ready when you want to use them. Service Hibernation will unload your services after a specified amount. This is useful to save RAM or keeping services from slowing down your computer.", |
255 | "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable", | 257 | "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable", |
@@ -262,7 +264,7 @@ | |||
262 | "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.", | 264 | "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.", |
263 | "settings.app.sentryInfo": "Sending telemetry data allows us to find errors in Ferdi - we will not send any personal information like your message data!", | 265 | "settings.app.sentryInfo": "Sending telemetry data allows us to find errors in Ferdi - we will not send any personal information like your message data!", |
264 | "settings.app.spellCheckerLanguageInfo": "Ferdi uses your Mac's build-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", | 266 | "settings.app.spellCheckerLanguageInfo": "Ferdi uses your Mac's build-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", |
265 | "settings.app.subheadlineCache": "Cache", | 267 | "settings.app.subheadlineCache": "å¿«å–", |
266 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", | 268 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", |
267 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature.", | 269 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature.", |
268 | "settings.app.translationHelp": "Help us to translate Ferdi into your language.", | 270 | "settings.app.translationHelp": "Help us to translate Ferdi into your language.", |
@@ -270,14 +272,14 @@ | |||
270 | "settings.app.updateStatusAvailable": "有å¯ç”¨æ›´æ–°ï¼Œä¸‹è¼‰ä¸...", | 272 | "settings.app.updateStatusAvailable": "有å¯ç”¨æ›´æ–°ï¼Œä¸‹è¼‰ä¸...", |
271 | "settings.app.updateStatusSearching": "檢查更新ä¸...", | 273 | "settings.app.updateStatusSearching": "檢查更新ä¸...", |
272 | "settings.app.updateStatusUpToDate": "已經是最新版本了", | 274 | "settings.app.updateStatusUpToDate": "已經是最新版本了", |
273 | "settings.invite.headline": "Invite Friends", | 275 | "settings.invite.headline": "邀請好å‹", |
274 | "settings.navigation.account": "帳戶", | 276 | "settings.navigation.account": "帳戶", |
275 | "settings.navigation.availableServices": "å¯ç”¨æœå‹™", | 277 | "settings.navigation.availableServices": "å¯ç”¨æœå‹™", |
276 | "settings.navigation.logout": "登出", | 278 | "settings.navigation.logout": "登出", |
277 | "settings.navigation.supportFerdi": "關於 Ferdi", | 279 | "settings.navigation.supportFerdi": "關於 Ferdi", |
278 | "settings.navigation.team": "管ç†åœ˜éšŠ", | 280 | "settings.navigation.team": "管ç†åœ˜éšŠ", |
279 | "settings.navigation.yourServices": "您的æœå‹™", | 281 | "settings.navigation.yourServices": "您的æœå‹™", |
280 | "settings.navigation.yourWorkspaces": "Your workspaces", | 282 | "settings.navigation.yourWorkspaces": "您的工作å€", |
281 | "settings.recipes.all": "所有æœå‹™", | 283 | "settings.recipes.all": "所有æœå‹™", |
282 | "settings.recipes.custom": "Custom Services", | 284 | "settings.recipes.custom": "Custom Services", |
283 | "settings.recipes.customService.headline.communityRecipes": "Community 3rd Party Recipes", | 285 | "settings.recipes.customService.headline.communityRecipes": "Community 3rd Party Recipes", |
@@ -285,12 +287,12 @@ | |||
285 | "settings.recipes.customService.headline.devRecipes": "Your Development Service Recipes", | 287 | "settings.recipes.customService.headline.devRecipes": "Your Development Service Recipes", |
286 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", | 288 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", |
287 | "settings.recipes.customService.openDevDocs": "Developer Documentation", | 289 | "settings.recipes.customService.openDevDocs": "Developer Documentation", |
288 | "settings.recipes.customService.openFolder": "Open folder", | 290 | "settings.recipes.customService.openFolder": "開啟資料夾", |
289 | "settings.recipes.headline": "å¯ç”¨æœå‹™", | 291 | "settings.recipes.headline": "å¯ç”¨æœå‹™", |
290 | "settings.recipes.missingService": "Missing a service?", | 292 | "settings.recipes.missingService": "Missing a service?", |
291 | "settings.recipes.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 293 | "settings.recipes.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", |
292 | "settings.recipes.servicesSuccessfulAddedInfo": "新增æœå‹™æˆåŠŸ", | 294 | "settings.recipes.servicesSuccessfulAddedInfo": "新增æœå‹™æˆåŠŸ", |
293 | "settings.searchService": "Search service", | 295 | "settings.searchService": "æœå°‹æœå‹™", |
294 | "settings.service.error.goBack": "返回", | 296 | "settings.service.error.goBack": "返回", |
295 | "settings.service.error.headline": "錯誤", | 297 | "settings.service.error.headline": "錯誤", |
296 | "settings.service.error.message": "無法載入æœå‹™å…ƒä»¶", | 298 | "settings.service.error.message": "無法載入æœå‹™å…ƒä»¶", |
@@ -301,14 +303,15 @@ | |||
301 | "settings.service.form.darkReaderBrightness": "Dark Reader Brightness", | 303 | "settings.service.form.darkReaderBrightness": "Dark Reader Brightness", |
302 | "settings.service.form.darkReaderContrast": "Dark Reader Contrast", | 304 | "settings.service.form.darkReaderContrast": "Dark Reader Contrast", |
303 | "settings.service.form.darkReaderSepia": "Dark Reader Sepia", | 305 | "settings.service.form.darkReaderSepia": "Dark Reader Sepia", |
304 | "settings.service.form.deleteButton": "Delete service", | 306 | "settings.service.form.deleteButton": "刪除æœå‹™", |
305 | "settings.service.form.editServiceHeadline": "編輯 {name}", | 307 | "settings.service.form.editServiceHeadline": "編輯 {name}", |
306 | "settings.service.form.enableAudio": "啟用音效", | 308 | "settings.service.form.enableAudio": "啟用音效", |
307 | "settings.service.form.enableBadge": "Show unread message badges", | 309 | "settings.service.form.enableBadge": "Show unread message badges", |
308 | "settings.service.form.enableDarkMode": "啟用夜間模å¼", | 310 | "settings.service.form.enableDarkMode": "啟用夜間模å¼", |
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "å•“ç”¨ä¼‘çœ ", |
310 | "settings.service.form.enableNotification": "啟用通知", | 312 | "settings.service.form.enableNotification": "啟用通知", |
311 | "settings.service.form.enableService": "啟用æœå‹™", | 313 | "settings.service.form.enableService": "啟用æœå‹™", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", |
314 | "settings.service.form.headlineGeneral": "一般", | 317 | "settings.service.form.headlineGeneral": "一般", |
@@ -320,19 +323,19 @@ | |||
320 | "settings.service.form.indirectMessages": "é‡å°å…¨éƒ¨è¨Šæ¯é¡¯ç¤ºé€šçŸ¥", | 323 | "settings.service.form.indirectMessages": "é‡å°å…¨éƒ¨è¨Šæ¯é¡¯ç¤ºé€šçŸ¥", |
321 | "settings.service.form.isHibernatedEnabledInfo": "When enabled, a service will be shut down after a period of time to save system resources.", | 324 | "settings.service.form.isHibernatedEnabledInfo": "When enabled, a service will be shut down after a period of time to save system resources.", |
322 | "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", | 325 | "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", |
323 | "settings.service.form.name": "åå", | 326 | "settings.service.form.name": "åå—", |
324 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", | 327 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", |
325 | "settings.service.form.openDarkmodeCss": "Open darkmode.css", | 328 | "settings.service.form.openDarkmodeCss": "é–‹å•Ÿ darkmode.css", |
326 | "settings.service.form.openUserCss": "Open user.css", | 329 | "settings.service.form.openUserCss": "é–‹å•Ÿ user.css", |
327 | "settings.service.form.openUserJs": "Open user.js", | 330 | "settings.service.form.openUserJs": "é–‹å•Ÿ user.js", |
328 | "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", | 331 | "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", |
329 | "settings.service.form.proxy.host": "Proxy Host/IP", | 332 | "settings.service.form.proxy.host": "Proxy Host/IP", |
330 | "settings.service.form.proxy.info": "Proxy settings will not be synchronized with the Ferdi servers.", | 333 | "settings.service.form.proxy.info": "Proxy settings will not be synchronized with the Ferdi servers.", |
331 | "settings.service.form.proxy.isEnabled": "使用代ç†", | 334 | "settings.service.form.proxy.isEnabled": "使用代ç†", |
332 | "settings.service.form.proxy.password": "Password (optional)", | 335 | "settings.service.form.proxy.password": "密碼 (é¸å¡«)", |
333 | "settings.service.form.proxy.port": "é€£æŽ¥åŸ ", | 336 | "settings.service.form.proxy.port": "é€£æŽ¥åŸ ", |
334 | "settings.service.form.proxy.restartInfo": "Please restart Ferdi after changing proxy Settings.", | 337 | "settings.service.form.proxy.restartInfo": "Please restart Ferdi after changing proxy Settings.", |
335 | "settings.service.form.proxy.user": "User (optional)", | 338 | "settings.service.form.proxy.user": "使用者 (é¸å¡«)", |
336 | "settings.service.form.recipeFileInfo": "Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.", | 339 | "settings.service.form.recipeFileInfo": "Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.", |
337 | "settings.service.form.saveButton": "儲å˜", | 340 | "settings.service.form.saveButton": "儲å˜", |
338 | "settings.service.form.tabHosted": "Hosted", | 341 | "settings.service.form.tabHosted": "Hosted", |
@@ -355,14 +358,14 @@ | |||
355 | "settings.supportFerdi.headline": "關於 Ferdi", | 358 | "settings.supportFerdi.headline": "關於 Ferdi", |
356 | "settings.supportFerdi.openSurvey": "Open survey", | 359 | "settings.supportFerdi.openSurvey": "Open survey", |
357 | "settings.supportFerdi.textDonation": "If you feel like supporting Ferdi development with a donation, you can do so on both,", | 360 | "settings.supportFerdi.textDonation": "If you feel like supporting Ferdi development with a donation, you can do so on both,", |
358 | "settings.supportFerdi.textDonationAnd": "and", | 361 | "settings.supportFerdi.textDonationAnd": "和", |
359 | "settings.supportFerdi.textExpenses": "While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our", | 362 | "settings.supportFerdi.textExpenses": "While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our", |
360 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", | 363 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", |
361 | "settings.supportFerdi.textListContributors": "Full list of contributors", | 364 | "settings.supportFerdi.textListContributors": "Full list of contributors", |
362 | "settings.supportFerdi.textListContributorsHere": "here", | 365 | "settings.supportFerdi.textListContributorsHere": "這裡", |
363 | "settings.supportFerdi.textOpenCollective": "Open Collective", | 366 | "settings.supportFerdi.textOpenCollective": "Open Collective", |
364 | "settings.supportFerdi.textSupportWelcome": "Support is always welcome. You can find a list of the help we need", | 367 | "settings.supportFerdi.textSupportWelcome": "Support is always welcome. You can find a list of the help we need", |
365 | "settings.supportFerdi.textSupportWelcomeHere": "here", | 368 | "settings.supportFerdi.textSupportWelcomeHere": "這裡", |
366 | "settings.supportFerdi.textVolunteers": "The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.", | 369 | "settings.supportFerdi.textVolunteers": "The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.", |
367 | "settings.supportFerdi.title": "Do you like Ferdi?", | 370 | "settings.supportFerdi.title": "Do you like Ferdi?", |
368 | "settings.team.contentHeadline": "Franz Team Management", | 371 | "settings.team.contentHeadline": "Franz Team Management", |
@@ -378,20 +381,20 @@ | |||
378 | "settings.user.form.accountType.non-profit": "éžç‡Ÿåˆ©", | 381 | "settings.user.form.accountType.non-profit": "éžç‡Ÿåˆ©", |
379 | "settings.user.form.currentPassword": "舊密碼", | 382 | "settings.user.form.currentPassword": "舊密碼", |
380 | "settings.user.form.email": "é›»å郵件信箱", | 383 | "settings.user.form.email": "é›»å郵件信箱", |
381 | "settings.user.form.firstname": "First Name", | 384 | "settings.user.form.firstname": "åå—", |
382 | "settings.user.form.lastname": "Last Name", | 385 | "settings.user.form.lastname": "姓æ°", |
383 | "settings.user.form.newPassword": "新密碼", | 386 | "settings.user.form.newPassword": "新密碼", |
384 | "settings.workspace.add.form.name": "åå", | 387 | "settings.workspace.add.form.name": "åå—", |
385 | "settings.workspace.add.form.submitButton": "建立工作å€", | 388 | "settings.workspace.add.form.submitButton": "建立工作å€", |
386 | "settings.workspace.form.buttonDelete": "刪除工作å€", | 389 | "settings.workspace.form.buttonDelete": "刪除工作å€", |
387 | "settings.workspace.form.buttonSave": "儲å˜å·¥ä½œå€åŸŸ", | 390 | "settings.workspace.form.buttonSave": "儲å˜å·¥ä½œå€åŸŸ", |
388 | "settings.workspace.form.keepLoaded": "Keep this workspace loaded*", | 391 | "settings.workspace.form.keepLoaded": "Keep this workspace loaded*", |
389 | "settings.workspace.form.keepLoadedInfo": "*This option will be overwritten by the global \"Keep all workspaces loaded\" option.", | 392 | "settings.workspace.form.keepLoadedInfo": "*This option will be overwritten by the global \"Keep all workspaces loaded\" option.", |
390 | "settings.workspace.form.name": "åå", | 393 | "settings.workspace.form.name": "åå—", |
391 | "settings.workspace.form.servicesInWorkspaceHeadline": "Services in this Workspace", | 394 | "settings.workspace.form.servicesInWorkspaceHeadline": "Services in this Workspace", |
392 | "settings.workspace.form.yourWorkspaces": "Your workspaces", | 395 | "settings.workspace.form.yourWorkspaces": "您的工作å€", |
393 | "settings.workspaces.deletedInfo": "Workspace has been deleted", | 396 | "settings.workspaces.deletedInfo": "Workspace has been deleted", |
394 | "settings.workspaces.headline": "Your workspaces", | 397 | "settings.workspaces.headline": "您的工作å€", |
395 | "settings.workspaces.noWorkspacesAdded": "You haven't created any workspaces yet.", | 398 | "settings.workspaces.noWorkspacesAdded": "You haven't created any workspaces yet.", |
396 | "settings.workspaces.tryReloadWorkspaces": "å†è©¦ä¸€æ¬¡", | 399 | "settings.workspaces.tryReloadWorkspaces": "å†è©¦ä¸€æ¬¡", |
397 | "settings.workspaces.updatedInfo": "您的更改已經儲å˜", | 400 | "settings.workspaces.updatedInfo": "您的更改已經儲å˜", |
@@ -400,7 +403,7 @@ | |||
400 | "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces", | 403 | "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces", |
401 | "setupAssistant.headline": "Let's get started", | 404 | "setupAssistant.headline": "Let's get started", |
402 | "setupAssistant.subheadline": "Choose from our most used services and get back on top of your messaging now.", | 405 | "setupAssistant.subheadline": "Choose from our most used services and get back on top of your messaging now.", |
403 | "setupAssistant.submit.label": "Let's go", | 406 | "setupAssistant.submit.label": "讓我們開始å§", |
404 | "sidebar.addNewService": "æ·»åŠ æ–°æœå‹™", | 407 | "sidebar.addNewService": "æ·»åŠ æ–°æœå‹™", |
405 | "sidebar.closeTodosDrawer": "Close Ferdi Todos", | 408 | "sidebar.closeTodosDrawer": "Close Ferdi Todos", |
406 | "sidebar.closeWorkspaceDrawer": "關閉工作å€", | 409 | "sidebar.closeWorkspaceDrawer": "關閉工作å€", |
@@ -411,9 +414,9 @@ | |||
411 | "sidebar.unmuteApp": "啟用通知", | 414 | "sidebar.unmuteApp": "啟用通知", |
412 | "signup.email.label": "é›»å郵件信箱", | 415 | "signup.email.label": "é›»å郵件信箱", |
413 | "signup.emailDuplicate": "æ¤é›»å郵件信箱已被註冊", | 416 | "signup.emailDuplicate": "æ¤é›»å郵件信箱已被註冊", |
414 | "signup.firstname.label": "First Name", | 417 | "signup.firstname.label": "åå—", |
415 | "signup.headline": "註冊", | 418 | "signup.headline": "註冊", |
416 | "signup.lastname.label": "Last Name", | 419 | "signup.lastname.label": "姓æ°", |
417 | "signup.legal.info": "在建立帳戶åŒæ™‚,您åŒæ„:", | 420 | "signup.legal.info": "在建立帳戶åŒæ™‚,您åŒæ„:", |
418 | "signup.legal.privacy": "éš±ç§è²æ˜Ž", | 421 | "signup.legal.privacy": "éš±ç§è²æ˜Ž", |
419 | "signup.legal.terms": "æœå‹™æ¢æ¬¾", | 422 | "signup.legal.terms": "æœå‹™æ¢æ¬¾", |
@@ -421,13 +424,13 @@ | |||
421 | "signup.password.label": "密碼", | 424 | "signup.password.label": "密碼", |
422 | "signup.submit.label": "建立帳戶", | 425 | "signup.submit.label": "建立帳戶", |
423 | "tabs.item.confirmDeleteService": "Do you really want to delete the {serviceName} service?", | 426 | "tabs.item.confirmDeleteService": "Do you really want to delete the {serviceName} service?", |
424 | "tabs.item.deleteService": "Delete service", | 427 | "tabs.item.deleteService": "刪除æœå‹™", |
425 | "tabs.item.disableAudio": "åœç”¨éŸ³æ•ˆ", | 428 | "tabs.item.disableAudio": "åœç”¨éŸ³æ•ˆ", |
426 | "tabs.item.disableDarkMode": "Disable Dark mode", | 429 | "tabs.item.disableDarkMode": "åœç”¨æ·±è‰²æ¨¡å¼", |
427 | "tabs.item.disableNotifications": "åœç”¨é€šçŸ¥", | 430 | "tabs.item.disableNotifications": "åœç”¨é€šçŸ¥", |
428 | "tabs.item.disableService": "Disable service", | 431 | "tabs.item.disableService": "åœç”¨æœå‹™", |
429 | "tabs.item.enableAudio": "啟用音效", | 432 | "tabs.item.enableAudio": "啟用音效", |
430 | "tabs.item.enableDarkMode": "Enable Dark mode", | 433 | "tabs.item.enableDarkMode": "啟用深色模å¼", |
431 | "tabs.item.enableNotification": "啟用通知", | 434 | "tabs.item.enableNotification": "啟用通知", |
432 | "tabs.item.enableService": "啟用æœå‹™", | 435 | "tabs.item.enableService": "啟用æœå‹™", |
433 | "tabs.item.hibernateService": "Hibernate service", | 436 | "tabs.item.hibernateService": "Hibernate service", |
diff --git a/src/i18n/locales/zh.json b/src/i18n/locales/zh.json index 648d75a6d..836a067f5 100644 --- a/src/i18n/locales/zh.json +++ b/src/i18n/locales/zh.json | |||
@@ -1,15 +1,15 @@ | |||
1 | { | 1 | { |
2 | "app.errorHandler.action": "é‡æ–°åŠ è½½", | 2 | "app.errorHandler.action": "é‡æ–°åŠ è½½", |
3 | "app.errorHandler.headline": "Something went wrong.", | 3 | "app.errorHandler.headline": "å‘生未知错误。", |
4 | "changeserver.customServerLabel": "Custom server", | 4 | "changeserver.customServerLabel": "自定义æœåŠ¡å™¨", |
5 | "changeserver.headline": "Change server", | 5 | "changeserver.headline": "更改æœåŠ¡å™¨", |
6 | "changeserver.label": "æœåŠ¡å™¨ï¼š", | 6 | "changeserver.label": "æœåŠ¡å™¨ï¼š", |
7 | "changeserver.urlError": "输入有效的URL", | 7 | "changeserver.urlError": "输入有效的URL", |
8 | "changeserver.warning": "Ferdiæ供的é¢å¤–设置将ä¸ä¼šè¢«ä¿å˜", | 8 | "changeserver.warning": "Ferdiæ供的é¢å¤–设置将ä¸ä¼šè¢«ä¿å˜", |
9 | "connectionLostBanner.cta": "é‡æ–°åŠ è½½æœåŠ¡", | 9 | "connectionLostBanner.cta": "é‡æ–°åŠ è½½æœåŠ¡", |
10 | "connectionLostBanner.informationLink": "å‘生了什么?", | 10 | "connectionLostBanner.informationLink": "å‘生了什么?", |
11 | "connectionLostBanner.message": "哦ä¸ï¼Ferdi失去了与 {name} 的连接。", | 11 | "connectionLostBanner.message": "哦ä¸ï¼Ferdi失去了与 {name} 的连接。", |
12 | "feature.basicAuth.signIn": "Sign In", | 12 | "feature.basicAuth.signIn": "登录", |
13 | "feature.nightlyBuilds.activate": "激活", | 13 | "feature.nightlyBuilds.activate": "激活", |
14 | "feature.nightlyBuilds.info": "æ¯å¤œç‰ˆ(Nightly builds) 是Ferdi的实验性版本,å¯èƒ½åŒ…å«æœªå®Œå–„或未完æˆçš„功能。这些æ¯å¤œç‰ˆä¸»è¦ç”±å¼€å‘人员æ¥æµ‹è¯•ä»–们新开å‘的功能åŠå®ƒä»¬åœ¨æœ€ç»ˆç‰ˆæœ¬çš„的表现。如果您ä¸çŸ¥é“自己在åšä»€ä¹ˆï¼Œæˆ‘们建议您ä¸è¦æ¿€æ´»æ¯å¤œç‰ˆã€‚", | 14 | "feature.nightlyBuilds.info": "æ¯å¤œç‰ˆ(Nightly builds) 是Ferdi的实验性版本,å¯èƒ½åŒ…å«æœªå®Œå–„或未完æˆçš„功能。这些æ¯å¤œç‰ˆä¸»è¦ç”±å¼€å‘人员æ¥æµ‹è¯•ä»–们新开å‘的功能åŠå®ƒä»¬åœ¨æœ€ç»ˆç‰ˆæœ¬çš„的表现。如果您ä¸çŸ¥é“自己在åšä»€ä¹ˆï¼Œæˆ‘们建议您ä¸è¦æ¿€æ´»æ¯å¤œç‰ˆã€‚", |
15 | "feature.nightlyBuilds.title": "æ¯å¤œç‰ˆ", | 15 | "feature.nightlyBuilds.title": "æ¯å¤œç‰ˆ", |
@@ -18,35 +18,35 @@ | |||
18 | "feature.publishDebugInfo.privacy": "éšç§æƒæ”¿ç–", | 18 | "feature.publishDebugInfo.privacy": "éšç§æƒæ”¿ç–", |
19 | "feature.publishDebugInfo.publish": "接å—并å‘布", | 19 | "feature.publishDebugInfo.publish": "接å—并å‘布", |
20 | "feature.publishDebugInfo.published": "您的调试日志已ç»å‘布,现在å¯ç”¨äºŽ", | 20 | "feature.publishDebugInfo.published": "您的调试日志已ç»å‘布,现在å¯ç”¨äºŽ", |
21 | "feature.publishDebugInfo.terms": "Terms of service", | 21 | "feature.publishDebugInfo.terms": "æœåŠ¡æ¡æ¬¾", |
22 | "feature.publishDebugInfo.title": "å‘布调试信æ¯", | 22 | "feature.publishDebugInfo.title": "å‘布调试信æ¯", |
23 | "feature.quickSwitch.info": "使用 TAB ,↑ å’Œ ↓ 选择æœåŠ¡ã€‚使用回车键(ENTER)打开æœåŠ¡", | 23 | "feature.quickSwitch.info": "使用 TAB ,↑ å’Œ ↓ 选择æœåŠ¡ã€‚使用回车键(ENTER)打开æœåŠ¡", |
24 | "feature.quickSwitch.search": "æœç´¢...", | 24 | "feature.quickSwitch.search": "æœç´¢...", |
25 | "feature.quickSwitch.title": "快速切æ¢", | 25 | "feature.quickSwitch.title": "快速切æ¢", |
26 | "global.api.unhealthy": "Can't connect to Ferdi online services", | 26 | "global.api.unhealthy": "æ— æ³•é“¾æŽ¥åˆ° Ferdi 在线æœåŠ¡", |
27 | "global.cancel": "Cancel", | 27 | "global.cancel": "å–消", |
28 | "global.edit": "编辑", | 28 | "global.edit": "编辑", |
29 | "global.no": "No", | 29 | "global.no": "å¦", |
30 | "global.notConnectedToTheInternet": "您没有连接到互è”网。", | 30 | "global.notConnectedToTheInternet": "您没有连接到互è”网。", |
31 | "global.ok": "Ok", | 31 | "global.ok": "确定", |
32 | "global.quit": "Quit", | 32 | "global.quit": "退出", |
33 | "global.quitConfirmation": "Do you really want to quit Ferdi?", | 33 | "global.quitConfirmation": "确定è¦é€€å‡ºå—?", |
34 | "global.save": "Save", | 34 | "global.save": "ä¿å˜", |
35 | "global.settings": "Settings", | 35 | "global.settings": "设置", |
36 | "global.spellchecker.useDefault": "使用系统默认值 ({default})", | 36 | "global.spellchecker.useDefault": "使用系统默认值 ({default})", |
37 | "global.spellchecking.autodetect": "自动检测è¯è¨€", | 37 | "global.spellchecking.autodetect": "自动检测è¯è¨€", |
38 | "global.spellchecking.autodetect.short": "自动", | 38 | "global.spellchecking.autodetect.short": "自动", |
39 | "global.spellchecking.language": "拼写检查è¯è¨€", | 39 | "global.spellchecking.language": "拼写检查è¯è¨€", |
40 | "global.submit": "Submit", | 40 | "global.submit": "æ交", |
41 | "global.userAgentHelp": "使用 'https://whatmyuseragent.com/' (以å‘现)或 'https://developers.whatismybrowser.com/useragents/explore/' (以选择) ä½ æ‰€éœ€è¦çš„用户代ç†å¹¶å¤åˆ¶ç²˜è´´åˆ°è¿™é‡Œã€‚", | 41 | "global.userAgentHelp": "使用 'https://whatmyuseragent.com/' (以å‘现)或 'https://developers.whatismybrowser.com/useragents/explore/' (以选择) ä½ æ‰€éœ€è¦çš„用户代ç†å¹¶å¤åˆ¶ç²˜è´´åˆ°è¿™é‡Œã€‚", |
42 | "global.userAgentPref": "æµè§ˆå™¨æ ‡è¯†ï¼ˆç”¨æˆ·ä»£ç†ï¼‰", | 42 | "global.userAgentPref": "æµè§ˆå™¨æ ‡è¯†ï¼ˆç”¨æˆ·ä»£ç†ï¼‰", |
43 | "global.yes": "Yes", | 43 | "global.yes": "是", |
44 | "import.headline": "å¯¼å…¥ä½ çš„ Ferdi 4 æœåŠ¡", | 44 | "import.headline": "å¯¼å…¥ä½ çš„ Ferdi 4 æœåŠ¡", |
45 | "import.notSupportedHeadline": "Ferdi 5尚未支æŒçš„æœåŠ¡", | 45 | "import.notSupportedHeadline": "Ferdi 5尚未支æŒçš„æœåŠ¡", |
46 | "import.skip.label": "æˆ‘æƒ³æ‰‹åŠ¨æ·»åŠ æœåŠ¡", | 46 | "import.skip.label": "æˆ‘æƒ³æ‰‹åŠ¨æ·»åŠ æœåŠ¡", |
47 | "import.submit.label": "Import {count} services", | 47 | "import.submit.label": "导入 {count} æœåŠ¡", |
48 | "infobar.authRequestFailed": "å°è¯•è¿›è¡Œèº«ä»½éªŒè¯æ—¶å‡ºé”™ã€‚如果æ¤é”™è¯¯ä»å˜åœ¨ï¼Œè¯·å°è¯•æ³¨é”€å¹¶é‡æ–°ç™»å½•ã€‚", | 48 | "infobar.authRequestFailed": "å°è¯•è¿›è¡Œèº«ä»½éªŒè¯æ—¶å‡ºé”™ã€‚如果æ¤é”™è¯¯ä»å˜åœ¨ï¼Œè¯·å°è¯•æ³¨é”€å¹¶é‡æ–°ç™»å½•ã€‚", |
49 | "infobar.buttonChangelog": "What is new?", | 49 | "infobar.buttonChangelog": "新功能", |
50 | "infobar.buttonInstallUpdate": "é‡å¯å¹¶å®‰è£…æ›´æ–°", | 50 | "infobar.buttonInstallUpdate": "é‡å¯å¹¶å®‰è£…æ›´æ–°", |
51 | "infobar.buttonReloadServices": "é‡è½½æœåŠ¡", | 51 | "infobar.buttonReloadServices": "é‡è½½æœåŠ¡", |
52 | "infobar.hide": "éšè—", | 52 | "infobar.hide": "éšè—", |
@@ -59,398 +59,401 @@ | |||
59 | "invite.name.label": "å称", | 59 | "invite.name.label": "å称", |
60 | "invite.skip.label": "ç¨åŽå†åš", | 60 | "invite.skip.label": "ç¨åŽå†åš", |
61 | "invite.submit.label": "å‘é€é‚€è¯·", | 61 | "invite.submit.label": "å‘é€é‚€è¯·", |
62 | "invite.successInfo": "Invitations sent successfully", | 62 | "invite.successInfo": "å·²æˆåŠŸå‘é€é‚€è¯·ã€‚", |
63 | "locked.headline": "å·²é”定", | 63 | "locked.headline": "å·²é”定", |
64 | "locked.info": "Ferdi is currently locked. Please unlock Ferdi with your password to see your messages.", | 64 | "locked.info": "Ferdiç›®å‰è¢«é”定。请使用密ç 解é”Ferdi以查看您的消æ¯ã€‚", |
65 | "locked.invalidCredentials": "Password invalid", | 65 | "locked.invalidCredentials": "密ç æ— æ•ˆ", |
66 | "locked.password.label": "Password", | 66 | "locked.password.label": "密ç ", |
67 | "locked.submit.label": "Unlock", | 67 | "locked.submit.label": "解é”", |
68 | "locked.touchId": "Unlock with Touch ID", | 68 | "locked.touchId": "使用 Touch ID 解é”", |
69 | "locked.touchIdPrompt": "unlock via Touch ID", | 69 | "locked.touchIdPrompt": "使用 Touch ID 解é”", |
70 | "locked.unlockWithPassword": "Unlock with Password", | 70 | "locked.unlockWithPassword": "使用密ç 解é”", |
71 | "login.changeServer": "Change server", | 71 | "login.changeServer": "更改æœåŠ¡å™¨", |
72 | "login.customServerQuestion": "Using a custom Ferdi server?", | 72 | "login.customServerQuestion": "Using a custom Ferdi server?", |
73 | "login.customServerSuggestion": "Try importing your Franz account", | 73 | "login.customServerSuggestion": "Try importing your Franz account", |
74 | "login.email.label": "电å邮箱地å€", | 74 | "login.email.label": "电å邮箱地å€", |
75 | "login.headline": "Sign in", | 75 | "login.headline": "登录", |
76 | "login.invalidCredentials": "Email or password not valid", | 76 | "login.invalidCredentials": "电å邮件或密ç æ— æ•ˆ", |
77 | "login.link.password": "Reset password", | 77 | "login.link.password": "修改密ç ", |
78 | "login.link.signup": "Create a free account", | 78 | "login.link.signup": "创建å…费账户", |
79 | "login.password.label": "Password", | 79 | "login.password.label": "密ç ", |
80 | "login.serverLogout": "Your session expired, please login again.", | 80 | "login.serverLogout": "您的登录信æ¯å·²è¿‡æœŸï¼Œè¯·é‡æ–°ç™»å½•ã€‚", |
81 | "login.submit.label": "Sign in", | 81 | "login.submit.label": "登录", |
82 | "login.tokenExpired": "Your session expired, please login again.", | 82 | "login.tokenExpired": "您的登录信æ¯å·²è¿‡æœŸï¼Œè¯·é‡æ–°ç™»å½•ã€‚", |
83 | "menu.Todoss.closeTodosDrawer": "Close Todos drawer", | 83 | "menu.Todoss.closeTodosDrawer": "å…³é—Todos抽屉æ ", |
84 | "menu.Todoss.openTodosDrawer": "Open Todos drawer", | 84 | "menu.Todoss.openTodosDrawer": "打开Todos抽屉æ ", |
85 | "menu.app.about": "About Ferdi", | 85 | "menu.app.about": "关于 Ferdi", |
86 | "menu.app.autohideMenuBar": "Auto-hide menu bar", | 86 | "menu.app.autohideMenuBar": "自动éšè—èœå•æ ", |
87 | "menu.app.checkForUpdates": "Check for updates", | 87 | "menu.app.checkForUpdates": "检查更新", |
88 | "menu.app.hide": "éšè—", | 88 | "menu.app.hide": "éšè—", |
89 | "menu.app.hideOthers": "Hide Others", | 89 | "menu.app.hideOthers": "éšè—其它", |
90 | "menu.app.unhide": "Unhide", | 90 | "menu.app.unhide": "å–消éšè—", |
91 | "menu.edit": "编辑", | 91 | "menu.edit": "编辑", |
92 | "menu.edit.copy": "Copy", | 92 | "menu.edit.copy": "å¤åˆ¶", |
93 | "menu.edit.cut": "Cut", | 93 | "menu.edit.cut": "剪切", |
94 | "menu.edit.delete": "åˆ é™¤", | 94 | "menu.edit.delete": "åˆ é™¤", |
95 | "menu.edit.emojiSymbols": "Emoji 和符å·", | 95 | "menu.edit.emojiSymbols": "Emoji 和符å·", |
96 | "menu.edit.findInPage": "页内æœç´¢", | 96 | "menu.edit.findInPage": "页内æœç´¢", |
97 | "menu.edit.paste": "Paste", | 97 | "menu.edit.paste": "粘贴", |
98 | "menu.edit.pasteAndMatchStyle": "Paste And Match Style", | 98 | "menu.edit.pasteAndMatchStyle": "粘贴并匹é…æ ·å¼", |
99 | "menu.edit.redo": "Redo", | 99 | "menu.edit.redo": "æ¢å¤", |
100 | "menu.edit.selectAll": "Select All", | 100 | "menu.edit.selectAll": "全选", |
101 | "menu.edit.speech": "è¯éŸ³", | 101 | "menu.edit.speech": "è¯éŸ³", |
102 | "menu.edit.startDictation": "Start Dictation", | 102 | "menu.edit.startDictation": "Start Dictation", |
103 | "menu.edit.startSpeaking": "开始说è¯", | 103 | "menu.edit.startSpeaking": "开始说è¯", |
104 | "menu.edit.stopSpeaking": "åœæ¢è¯´è¯", | 104 | "menu.edit.stopSpeaking": "åœæ¢è¯´è¯", |
105 | "menu.edit.undo": "Undo", | 105 | "menu.edit.undo": "撤销", |
106 | "menu.file": "文件", | 106 | "menu.file": "文件", |
107 | "menu.help": "Help", | 107 | "menu.help": "帮助", |
108 | "menu.help.changelog": "更新日志", | 108 | "menu.help.changelog": "更新日志", |
109 | "menu.help.debugInfo": "å¤åˆ¶è°ƒè¯•ä¿¡æ¯", | 109 | "menu.help.debugInfo": "å¤åˆ¶è°ƒè¯•ä¿¡æ¯", |
110 | "menu.help.debugInfoCopiedBody": "Your Debug Information has been copied to your clipboard.", | 110 | "menu.help.debugInfoCopiedBody": "您的调试信æ¯å·²å¤åˆ¶åˆ°å‰ªè´´æ¿ã€‚", |
111 | "menu.help.debugInfoCopiedHeadline": "Ferdi Debug Information", | 111 | "menu.help.debugInfoCopiedHeadline": "å¤åˆ¶è°ƒè¯•ä¿¡æ¯", |
112 | "menu.help.importExportData": "Import/Export Configuration Data", | 112 | "menu.help.importExportData": "导入/导出é…置数æ®", |
113 | "menu.help.learnMore": "Learn More", | 113 | "menu.help.learnMore": "了解更多", |
114 | "menu.help.privacy": "Privacy Statement", | 114 | "menu.help.privacy": "éšç§å£°æ˜Ž", |
115 | "menu.help.publishDebugInfo": "å‘布调试信æ¯", | 115 | "menu.help.publishDebugInfo": "å‘布调试信æ¯", |
116 | "menu.help.support": "支æŒ", | 116 | "menu.help.support": "支æŒ", |
117 | "menu.help.tos": "æœåŠ¡æ¡æ¬¾", | 117 | "menu.help.tos": "æœåŠ¡æ¡æ¬¾", |
118 | "menu.services": "æœåŠ¡", | 118 | "menu.services": "æœåŠ¡", |
119 | "menu.services.activatePreviousService": "Activate previous service", | 119 | "menu.services.activatePreviousService": "激活上一个æœåŠ¡", |
120 | "menu.services.addNewService": "Add New Service...", | 120 | "menu.services.addNewService": "æ·»åŠ æ–°æœåŠ¡", |
121 | "menu.services.goHome": "主页", | 121 | "menu.services.goHome": "主页", |
122 | "menu.services.setNextServiceActive": "Activate next service", | 122 | "menu.services.setNextServiceActive": "激活下一个æœåŠ¡", |
123 | "menu.todos": "待办事项", | 123 | "menu.todos": "待办事项", |
124 | "menu.todos.enableTodos": "å¯ç”¨å¾…办事项", | 124 | "menu.todos.enableTodos": "å¯ç”¨å¾…办事项", |
125 | "menu.view": "查看", | 125 | "menu.view": "查看", |
126 | "menu.view.back": "返回", | 126 | "menu.view.back": "返回", |
127 | "menu.view.forward": "Forward", | 127 | "menu.view.forward": "å‘å‰", |
128 | "menu.view.lockFerdi": "Lock Ferdi", | 128 | "menu.view.lockFerdi": "é”定Ferdi", |
129 | "menu.view.openQuickSwitch": "Open Quick Switch", | 129 | "menu.view.openQuickSwitch": "打开快速切æ¢", |
130 | "menu.view.reloadFerdi": "Reload Ferdi", | 130 | "menu.view.reloadFerdi": "é‡å¯Ferdi", |
131 | "menu.view.reloadService": "é‡æ–°åŠ è½½æœåŠ¡", | 131 | "menu.view.reloadService": "é‡æ–°åŠ è½½æœåŠ¡", |
132 | "menu.view.reloadTodos": "Reload ToDos", | 132 | "menu.view.reloadTodos": "é‡å¯ToDos", |
133 | "menu.view.resetZoom": "Actual Size", | 133 | "menu.view.resetZoom": "实际尺寸", |
134 | "menu.view.toggleDarkMode": "Toggle Dark Mode", | 134 | "menu.view.toggleDarkMode": "切æ¢æš—色主题", |
135 | "menu.view.toggleDevTools": "Toggle Developer Tools", | 135 | "menu.view.toggleDevTools": "切æ¢å¼€å‘人员工具", |
136 | "menu.view.toggleFullScreen": "Toggle Full Screen", | 136 | "menu.view.toggleFullScreen": "切æ¢å…¨å±", |
137 | "menu.view.toggleServiceDevTools": "Toggle Service Developer Tools", | 137 | "menu.view.toggleServiceDevTools": "切æ¢æœåŠ¡å¼€å‘工具", |
138 | "menu.view.toggleTodosDevTools": "Toggle Todos Developer Tools", | 138 | "menu.view.toggleTodosDevTools": "切æ¢Todoså¼€å‘工具", |
139 | "menu.view.zoomIn": "Zoom In", | 139 | "menu.view.zoomIn": "放大", |
140 | "menu.view.zoomOut": "Zoom Out", | 140 | "menu.view.zoomOut": "缩å°", |
141 | "menu.window": "Window", | 141 | "menu.window": "窗å£", |
142 | "menu.window.close": "Close", | 142 | "menu.window.close": "å…³é—", |
143 | "menu.window.minimize": "Minimize", | 143 | "menu.window.minimize": "最å°åŒ–", |
144 | "menu.workspaces": "Workspaces", | 144 | "menu.workspaces": "工作区", |
145 | "menu.workspaces.addNewWorkspace": "Add New Workspace...", | 145 | "menu.workspaces.addNewWorkspace": "新建工作区…", |
146 | "menu.workspaces.closeWorkspaceDrawer": "Close workspace drawer", | 146 | "menu.workspaces.closeWorkspaceDrawer": "å…³é—工作区抽屉æ ", |
147 | "menu.workspaces.defaultWorkspace": "All services", | 147 | "menu.workspaces.defaultWorkspace": "所有æœåŠ¡", |
148 | "menu.workspaces.openWorkspaceDrawer": "Open workspace drawer", | 148 | "menu.workspaces.openWorkspaceDrawer": "打开工作区抽屉æ ", |
149 | "password.email.label": "电å邮箱地å€", | 149 | "password.email.label": "电å邮箱地å€", |
150 | "password.headline": "Reset password", | 150 | "password.headline": "修改密ç ", |
151 | "password.link.login": "Sign in to your account", | 151 | "password.link.login": "登录到您的å¸æˆ·", |
152 | "password.link.signup": "Create a free account", | 152 | "password.link.signup": "创建å…费账户", |
153 | "password.noUser": "No user with that email address was found", | 153 | "password.noUser": "找ä¸åˆ°è¯¥ç”µå邮件地å€çš„用户", |
154 | "password.successInfo": "Your new password was sent to your email address", | 154 | "password.successInfo": "新密ç å·²å‘é€åˆ°æ‚¨çš„邮箱", |
155 | "service.crashHandler.action": "Reload {name}", | 155 | "service.crashHandler.action": "é‡æ–°åŠ è½½{name}", |
156 | "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds", | 156 | "service.crashHandler.autoReload": "å°è¯•åœ¨ {name} 秒åŽè‡ªåŠ¨æ¢å¤ {seconds}", |
157 | "service.crashHandler.headline": "Oh no!", | 157 | "service.crashHandler.headline": "糟糕ï¼", |
158 | "service.crashHandler.text": "{name} has caused an error.", | 158 | "service.crashHandler.text": "{name} é€ æˆäº†ä¸€ä¸ªé”™è¯¯ã€‚", |
159 | "service.disabledHandler.action": "Enable {name}", | 159 | "service.disabledHandler.action": "å¯ç”¨ {name}", |
160 | "service.disabledHandler.headline": "{name} is disabled", | 160 | "service.disabledHandler.headline": "{name} å·²ç¦ç”¨", |
161 | "service.errorHandler.action": "Reload {name}", | 161 | "service.errorHandler.action": "é‡æ–°åŠ è½½{name}", |
162 | "service.errorHandler.editAction": "Edit {name}", | 162 | "service.errorHandler.editAction": "编辑 {name}", |
163 | "service.errorHandler.headline": "Oh no!", | 163 | "service.errorHandler.headline": "糟糕ï¼", |
164 | "service.errorHandler.message": "Error", | 164 | "service.errorHandler.message": "错误", |
165 | "service.errorHandler.text": "{name} has failed to load.", | 165 | "service.errorHandler.text": "{name} åŠ è½½å¤±è´¥ã€‚", |
166 | "service.webviewLoader.loading": "Loading {service}", | 166 | "service.webviewLoader.loading": "æ£åœ¨åŠ è½½ {service}", |
167 | "services.getStarted": "Get started", | 167 | "services.getStarted": "马上体验", |
168 | "services.login": "Please login to use Ferdi.", | 168 | "services.login": "请登录åŽä½¿ç”¨ Ferdi。", |
169 | "services.serverInfo": "Optionally, you can change your Ferdi server by clicking the cog in the bottom left corner. If you are switching over (from one of the hosted servers) to using Ferdi without an account, please be informed that you can export your data from that server and subsequently import it using the Help menu to resurrect all your workspaces and configured services!", | 169 | "services.serverInfo": "或者,您å¯ä»¥é€šè¿‡å•å‡»å·¦ä¸‹è§’的齿轮æ¥æ›´æ”¹æ‚¨çš„ Ferdi æœåŠ¡å™¨ã€‚ 如果您è¦ï¼ˆä»Žä¸€å°æ‰˜ç®¡æœåŠ¡å™¨ï¼‰åˆ‡æ¢åˆ°æ²¡æœ‰å¸æˆ·çš„ Ferdi,请注æ„,您å¯ä»¥ä»Žè¯¥æœåŠ¡å™¨å¯¼å‡ºæ•°æ®ï¼Œç„¶åŽä½¿ç”¨â€œå¸®åŠ©â€èœå•å°†å…¶å¯¼å…¥ï¼Œä»¥æ¢å¤æ‰€æœ‰å·¥ä½œåŒºå’Œé…置的æœåŠ¡ï¼", |
170 | "services.serverless": "Use Ferdi without an Account", | 170 | "services.serverless": "使用ä¸å¸¦è´¦æˆ·çš„Ferdi", |
171 | "services.welcome": "Welcome to Ferdi", | 171 | "services.welcome": "欢迎使用 Ferdi", |
172 | "settings.account.account.editButton": "Edit account", | 172 | "settings.account.account.editButton": "编辑å¸æˆ·", |
173 | "settings.account.accountUnavailable": "Account is unavailable", | 173 | "settings.account.accountUnavailable": "账户ä¸å¯ç”¨", |
174 | "settings.account.accountUnavailableInfo": "You are using Ferdi without an account. If you want to use Ferdi with an account and keep your services synchronized across installations, please select a server in the Settings tab then login.", | 174 | "settings.account.accountUnavailableInfo": "You are using Ferdi without an account. If you want to use Ferdi with an account and keep your services synchronized across installations, please select a server in the Settings tab then login.", |
175 | "settings.account.buttonSave": "Update profile", | 175 | "settings.account.buttonSave": "æ›´æ–°é…置文件", |
176 | "settings.account.deleteAccount": "Delete account", | 176 | "settings.account.deleteAccount": "åˆ é™¤è´¦æˆ·", |
177 | "settings.account.deleteEmailSent": "You have received an email with a link to confirm your account deletion. Your account and data cannot be restored!", | 177 | "settings.account.deleteEmailSent": "您会收到一å°å«æœ‰ç¡®è®¤åˆ 除å¸æˆ·çš„电å邮件。您的å¸æˆ·å’Œæ•°æ®å°†æ— 法æ¢å¤ï¼", |
178 | "settings.account.deleteInfo": "If you don't need your Ferdi account any longer, you can delete your account and all related data here.", | 178 | "settings.account.deleteInfo": "如果您ä¸å†éœ€è¦æ‚¨çš„ Ferdi å¸æˆ·ï¼Œæ‚¨å¯ä»¥åœ¨è¿™é‡Œåˆ 除您的å¸æˆ·å’Œæ‰€æœ‰ç›¸å…³æ•°æ®ã€‚", |
179 | "settings.account.headline": "Account", | 179 | "settings.account.headline": "账户", |
180 | "settings.account.headlineAccount": "Account information", | 180 | "settings.account.headlineAccount": "账户信æ¯", |
181 | "settings.account.headlineDangerZone": "Danger Zone", | 181 | "settings.account.headlineDangerZone": "å±é™©æ“作", |
182 | "settings.account.headlineInvoices": "Invoices", | 182 | "settings.account.headlineInvoices": "Invoices", |
183 | "settings.account.headlinePassword": "Change password", | 183 | "settings.account.headlinePassword": "更改密ç ", |
184 | "settings.account.headlineProfile": "Update profile", | 184 | "settings.account.headlineProfile": "æ›´æ–°é…置文件", |
185 | "settings.account.successInfo": "Your changes have been saved", | 185 | "settings.account.successInfo": "您的更改已ä¿å˜", |
186 | "settings.account.tryReloadServices": "Try again", | 186 | "settings.account.tryReloadServices": "é‡æ–°å°è¯•", |
187 | "settings.account.tryReloadUserInfoRequest": "Try again", | 187 | "settings.account.tryReloadUserInfoRequest": "é‡æ–°å°è¯•", |
188 | "settings.account.userInfoRequestFailed": "Could not load user information", | 188 | "settings.account.userInfoRequestFailed": "æ— æ³•åŠ è½½ç”¨æˆ·ä¿¡æ¯ã€‚", |
189 | "settings.account.yourLicense": "Your Ferdi License:", | 189 | "settings.account.yourLicense": "您的 Ferdi 许å¯è¯ï¼š", |
190 | "settings.app.accentColorInfo": "Write your accent color in a CSS-compatible format. (Default: {defaultAccentColor})", | 190 | "settings.app.accentColorInfo": "ä½ å¯ä»¥å†™å…¥ä¸€ä¸ªCSSæ ¼å¼æ‰€æ”¯æŒçš„强调色。(默认: {defaultAccentColor})", |
191 | "settings.app.buttonClearAllCache": "Clear cache", | 191 | "settings.app.buttonClearAllCache": "清除缓å˜", |
192 | "settings.app.buttonInstallUpdate": "é‡å¯å¹¶å®‰è£…æ›´æ–°", | 192 | "settings.app.buttonInstallUpdate": "é‡å¯å¹¶å®‰è£…æ›´æ–°", |
193 | "settings.app.buttonOpenFerdiProfileFolder": "Open Profile folder", | 193 | "settings.app.buttonOpenFerdiProfileFolder": "打开é…置文件所在目录", |
194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Open Service Recipes folder", | 194 | "settings.app.buttonOpenFerdiServiceRecipesFolder": "Open Service Recipes folder", |
195 | "settings.app.buttonSearchForUpdate": "Check for updates", | 195 | "settings.app.buttonSearchForUpdate": "检查更新", |
196 | "settings.app.cacheInfo": "Ferdi cache is currently using {size} of disk space.", | 196 | "settings.app.cacheInfo": "Ferdi 缓å˜ç›®å‰å 用了 {size} ç£ç›˜ç©ºé—´ã€‚", |
197 | "settings.app.cacheNotCleared": "Couldn't clear all cache", | 197 | "settings.app.cacheNotCleared": "æ— æ³•æ¸…é™¤æ‰€æœ‰ç¼“å˜", |
198 | "settings.app.closeSettings": "Close settings", | 198 | "settings.app.closeSettings": "å…³é—设置", |
199 | "settings.app.currentVersion": "Current version:", | 199 | "settings.app.currentVersion": "当å‰ç‰ˆæœ¬ï¼š", |
200 | "settings.app.form.accentColor": "Accent color", | 200 | "settings.app.form.accentColor": "强调色", |
201 | "settings.app.form.adaptableDarkMode": "Synchronize dark mode with my OS's dark mode setting", | 201 | "settings.app.form.adaptableDarkMode": "åŒæ¥ç³»ç»Ÿæš—色模å¼", |
202 | "settings.app.form.alwaysShowWorkspaces": "Always show workspace drawer", | 202 | "settings.app.form.alwaysShowWorkspaces": "总是显示工作区抽屉æ ", |
203 | "settings.app.form.autoLaunchInBackground": "Open in background", | 203 | "settings.app.form.autoLaunchInBackground": "在åŽå°æ‰“å¼€", |
204 | "settings.app.form.autoLaunchOnStart": "Launch Ferdi on start", | 204 | "settings.app.form.autoLaunchOnStart": "开机å¯åŠ¨Ferdi\n", |
205 | "settings.app.form.automaticUpdates": "Enable updates", | 205 | "settings.app.form.automaticUpdates": "å¯ç”¨æ›´æ–°", |
206 | "settings.app.form.beta": "Include beta versions", | 206 | "settings.app.form.beta": "包å«æµ‹è¯•ç‰ˆ", |
207 | "settings.app.form.clipboardNotifications": "Don't show notifications for clipboard events", | 207 | "settings.app.form.clipboardNotifications": "ä¸æ˜¾ç¤ºå‰ªè´´æ¿äº‹ä»¶çš„通知", |
208 | "settings.app.form.closeToSystemTray": "Close Ferdi to system tray", | 208 | "settings.app.form.closeToSystemTray": "å…³é—Ferdi到系统托盘", |
209 | "settings.app.form.confirmOnQuit": "Confirm when quitting Ferdi", | 209 | "settings.app.form.confirmOnQuit": "退出Ferdi 时确认", |
210 | "settings.app.form.customTodoServer": "Custom Todo Server", | 210 | "settings.app.form.customTodoServer": "自定义TodoæœåŠ¡å™¨", |
211 | "settings.app.form.darkMode": "Enable Dark Mode", | 211 | "settings.app.form.darkMode": "å¼€å¯æ·±è‰²ä¸»é¢˜", |
212 | "settings.app.form.enableGPUAcceleration": "Enable GPU Acceleration", | 212 | "settings.app.form.enableGPUAcceleration": "å¯ç”¨ GPU åŠ é€Ÿ", |
213 | "settings.app.form.enableLock": "Enable Password Lock", | 213 | "settings.app.form.enableGlobalHideShortcut": "å¯ç”¨å…¨å±€å¿«æ·é”®ä»¥éšè—Ferdi", |
214 | "settings.app.form.enableMenuBar": "Always show Ferdi in Menu Bar", | 214 | "settings.app.form.enableLock": "å¯ç”¨å¯†ç é”", |
215 | "settings.app.form.enableSpellchecking": "Enable spell checking", | 215 | "settings.app.form.enableLongPressServiceHint": "Enable service shortcut hint on long press", |
216 | "settings.app.form.enableSystemTray": "Always show Ferdi in System Tray", | 216 | "settings.app.form.enableMenuBar": "总是在èœå•æ ä¸æ˜¾ç¤º Ferdi", |
217 | "settings.app.form.enableTodos": "Enable Ferdi Todos", | 217 | "settings.app.form.enableSpellchecking": "å¯ç”¨æ‹¼å†™æ£€æŸ¥", |
218 | "settings.app.form.hibernateOnStartup": "Keep services in hibernation on startup", | 218 | "settings.app.form.enableSystemTray": "总是在系统托盘ä¸æ˜¾ç¤º Ferdi", |
219 | "settings.app.form.hibernationStrategy": "Hibernation strategy", | 219 | "settings.app.form.enableTodos": "å¯ç”¨ Ferdi Todos", |
220 | "settings.app.form.iconSize": "Service icon size", | 220 | "settings.app.form.hibernateOnStartup": "å¯åŠ¨æ—¶ä¿æŒä¼‘çœ æœåŠ¡", |
221 | "settings.app.form.inactivityLock": "Lock after inactivity", | 221 | "settings.app.form.hibernationStrategy": "ä¼‘çœ ç–ç•¥", |
222 | "settings.app.form.keepAllWorkspacesLoaded": "Keep all workspaces loaded", | 222 | "settings.app.form.iconSize": "æœåŠ¡å›¾æ ‡å¤§å°", |
223 | "settings.app.form.language": "Language", | 223 | "settings.app.form.inactivityLock": "自动é”定", |
224 | "settings.app.form.lockPassword": "Password", | 224 | "settings.app.form.keepAllWorkspacesLoaded": "ä¿æŒæ‰€æœ‰å·¥ä½œåŒºåŠ è½½", |
225 | "settings.app.form.minimizeToSystemTray": "Minimize Ferdi to system tray", | 225 | "settings.app.form.language": "è¯è¨€(Language)", |
226 | "settings.app.form.lockPassword": "密ç ", | ||
227 | "settings.app.form.minimizeToSystemTray": "最å°åŒ–Ferdi到系统托盘", | ||
226 | "settings.app.form.navigationBarBehaviour": "Navigation bar behaviour", | 228 | "settings.app.form.navigationBarBehaviour": "Navigation bar behaviour", |
227 | "settings.app.form.notifyTaskBarOnMessage": "Notify TaskBar/Dock on new message", | 229 | "settings.app.form.notifyTaskBarOnMessage": "Notify TaskBar/Dock on new message", |
228 | "settings.app.form.passwordToggle": "Password toggle", | 230 | "settings.app.form.passwordToggle": "Password toggle", |
229 | "settings.app.form.predefinedTodoServer": "Todo Server", | 231 | "settings.app.form.predefinedTodoServer": "TodoæœåŠ¡å™¨", |
230 | "settings.app.form.privateNotifications": "Don't show message content in notifications", | 232 | "settings.app.form.privateNotifications": "ä¸åœ¨é€šçŸ¥ä¸æ˜¾ç¤ºæ¶ˆæ¯å†…容", |
231 | "settings.app.form.reloadAfterResume": "Reload Ferdi after system resume", | 233 | "settings.app.form.reloadAfterResume": "系统æ¢å¤åŽé‡æ–°åŠ è½½ Ferdi", |
232 | "settings.app.form.runInBackground": "Keep Ferdi in background when closing the window", | 234 | "settings.app.form.runInBackground": "å…³é—窗å£æ—¶ä¿æŒFerdiåŽå°çŠ¶æ€", |
233 | "settings.app.form.scheduledDNDEnabled": "Enable scheduled Do-not-Disturb", | 235 | "settings.app.form.scheduledDNDEnabled": "Enable scheduled Do-not-Disturb", |
234 | "settings.app.form.scheduledDNDEnd": "To", | 236 | "settings.app.form.scheduledDNDEnd": "至", |
235 | "settings.app.form.scheduledDNDStart": "From", | 237 | "settings.app.form.scheduledDNDStart": "从", |
236 | "settings.app.form.searchEngine": "Search engine", | 238 | "settings.app.form.searchEngine": "æœç´¢å¼•æ“Ž", |
237 | "settings.app.form.sentry": "Send telemetry data", | 239 | "settings.app.form.sentry": "å‘é€é¥æµ‹æ•°æ®", |
238 | "settings.app.form.serviceRibbonWidth": "Sidebar width", | 240 | "settings.app.form.serviceRibbonWidth": "侧边æ 宽度", |
239 | "settings.app.form.showDisabledServices": "Display disabled services tabs", | 241 | "settings.app.form.showDisabledServices": "显示ç¦ç”¨çš„æœåŠ¡æ ‡ç¾", |
240 | "settings.app.form.showDragArea": "Show draggable area on window", | 242 | "settings.app.form.showDragArea": "在窗å£ä¸Šæ˜¾ç¤ºå¯æ‹–动区域", |
241 | "settings.app.form.showMessagesBadgesWhenMuted": "Show unread message badge when notifications are disabled", | 243 | "settings.app.form.showMessagesBadgesWhenMuted": "ç¦ç”¨é€šçŸ¥æ—¶æ˜¾ç¤ºæœªè¯»æ¶ˆæ¯å¾½ç« ", |
242 | "settings.app.form.splitMode": "Enable Split View Mode", | 244 | "settings.app.form.splitMode": "å¯ç”¨åˆ†å‰²è§†å›¾æ¨¡å¼", |
243 | "settings.app.form.startMinimized": "Start minimized", | 245 | "settings.app.form.startMinimized": "å¯åŠ¨æ—¶æœ€å°åŒ–", |
244 | "settings.app.form.universalDarkMode": "Enable universal Dark Mode", | 246 | "settings.app.form.universalDarkMode": "Enable universal Dark Mode", |
245 | "settings.app.form.useTouchIdToUnlock": "Allow using TouchID to unlock Ferdi", | 247 | "settings.app.form.useTouchIdToUnlock": "å…许使用 TouchID 解é”Ferdi", |
246 | "settings.app.form.useVerticalStyle": "Use horizontal style", | 248 | "settings.app.form.useVerticalStyle": "ä½¿ç”¨æ°´å¹³æ ·å¼", |
247 | "settings.app.form.wakeUpStrategy": "Wake up strategy", | 249 | "settings.app.form.wakeUpStrategy": "唤醒ç–ç•¥", |
248 | "settings.app.headlineAdvanced": "Advanced", | 250 | "settings.app.headlineAdvanced": "高级", |
249 | "settings.app.headlineAppearance": "Appearance", | 251 | "settings.app.headlineAppearance": "外观", |
250 | "settings.app.headlineGeneral": "General", | 252 | "settings.app.headlineGeneral": "一般信æ¯", |
251 | "settings.app.headlineLanguage": "Language", | 253 | "settings.app.headlineLanguage": "è¯è¨€(Language)", |
252 | "settings.app.headlinePrivacy": "Privacy", | 254 | "settings.app.headlinePrivacy": "éšç§æ”¿ç–", |
253 | "settings.app.headlineUpdates": "Updates", | 255 | "settings.app.headlineUpdates": "æ›´æ–°", |
254 | "settings.app.hibernateInfo": "By default, Ferdi will keep all your services open and loaded in the background so they are ready when you want to use them. Service Hibernation will unload your services after a specified amount. This is useful to save RAM or keeping services from slowing down your computer.", | 256 | "settings.app.hibernateInfo": "默认情况下,Ferdiå°†ä¿æŒæ‚¨æ‰€æœ‰çš„æœåŠ¡åœ¨åŽå°æ‰“å¼€å¹¶åŠ è½½ï¼Œè¿™æ ·å½“æ‚¨æƒ³è¦ä½¿ç”¨å®ƒä»¬æ—¶ä»–们就å¯ä»¥äº†ã€‚ æœåŠ¡ä¼‘çœ å°†åœ¨æŒ‡å®šæ•°é‡åŽå¸è½½æ‚¨çš„æœåŠ¡ã€‚这有助于ä¿å˜å†…å˜æˆ–ä¿æŒæœåŠ¡ä»¥å‡æ…¢æ‚¨çš„计算机速度。", |
255 | "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable", | 257 | "settings.app.inactivityLockInfo": "Minutes of inactivity, after which Ferdi should automatically lock. Use 0 to disable", |
256 | "settings.app.languageDisclaimer": "Official translations are English & German. All other languages are community based translations.", | 258 | "settings.app.languageDisclaimer": "官方翻译为英è¯å’Œå¾·è¯ï¼Œæ‰€æœ‰å…¶ä»–è¯è¨€éƒ½æ˜¯åŸºäºŽç¤¾åŒºçš„翻译。", |
257 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", | 259 | "settings.app.lockInfo": "Password Lock allows you to keep your messages protected.\nUsing Password Lock, you will be prompted to enter your password everytime you start Ferdi or lock Ferdi yourself using the lock symbol in the bottom left corner or the shortcut {lockShortcut}.", |
258 | "settings.app.lockedPassword": "Password", | 260 | "settings.app.lockedPassword": "密ç ", |
259 | "settings.app.lockedPasswordInfo": "Please make sure to set a password you'll remember.\nIf you loose this password, you will have to reinstall Ferdi.", | 261 | "settings.app.lockedPasswordInfo": "请务必设置您将记ä½çš„密ç 。\n如果您丢失了这个密ç ,您将ä¸å¾—ä¸é‡æ–°å®‰è£…Ferdi。", |
260 | "settings.app.restartRequired": "Changes require restart", | 262 | "settings.app.restartRequired": "当å‰çš„å˜æ›´éœ€è¦é‡æ–°å¯åŠ¨", |
261 | "settings.app.scheduledDNDInfo": "Scheduled Do-not-Disturb allows you to define a period of time in which you do not want to get Notifications from Ferdi.", | 263 | "settings.app.scheduledDNDInfo": "Scheduled Do-not-Disturb allows you to define a period of time in which you do not want to get Notifications from Ferdi.", |
262 | "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.", | 264 | "settings.app.scheduledDNDTimeInfo": "Times in 24-Hour-Format. End time can be before start time (e.g. start 17:00, end 09:00) to enable Do-not-Disturb overnight.", |
263 | "settings.app.sentryInfo": "Sending telemetry data allows us to find errors in Ferdi - we will not send any personal information like your message data!", | 265 | "settings.app.sentryInfo": "å‘é€é¥æµ‹æ•°æ®ä½¿æˆ‘们能够找到Ferdi错误——我们ä¸ä¼šå‘é€ä»»ä½•ä¸ªäººä¿¡æ¯ï¼Œå¦‚您的消æ¯æ•°æ®ï¼", |
264 | "settings.app.spellCheckerLanguageInfo": "Ferdi uses your Mac's build-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", | 266 | "settings.app.spellCheckerLanguageInfo": "Ferdi uses your Mac's build-in spellchecker to check for typos. If you want to change the languages the spellchecker checks for, you can do so in your Mac's System Preferences.", |
265 | "settings.app.subheadlineCache": "Cache", | 267 | "settings.app.subheadlineCache": "缓å˜", |
266 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", | 268 | "settings.app.subheadlineFerdiProfile": "Ferdi Profile", |
267 | "settings.app.todoServerInfo": "This server will be used for the \"Ferdi Todo\" feature.", | 269 | "settings.app.todoServerInfo": "æ¤æœåŠ¡å™¨å°†ç”¨äºŽ\"Ferdi Todo\"功能。", |
268 | "settings.app.translationHelp": "Help us to translate Ferdi into your language.", | 270 | "settings.app.translationHelp": "帮助我们翻译 Ferdi æˆæ‚¨çš„è¯è¨€", |
269 | "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.", | 271 | "settings.app.universalDarkModeInfo": "Universal Dark Mode tries to dynamically generate dark mode styles for services that are otherwise not currently supported.", |
270 | "settings.app.updateStatusAvailable": "Update available, downloading...", | 272 | "settings.app.updateStatusAvailable": "æ›´æ–°å¯ç”¨ï¼Œæ£åœ¨ä¸‹è½½...", |
271 | "settings.app.updateStatusSearching": "Is searching for update", | 273 | "settings.app.updateStatusSearching": "æ£åœ¨æœç´¢æ›´æ–°", |
272 | "settings.app.updateStatusUpToDate": "You are using the latest version of Ferdi", | 274 | "settings.app.updateStatusUpToDate": "您æ£åœ¨ä½¿ç”¨æœ€æ–°ç‰ˆæœ¬çš„Ferdi", |
273 | "settings.invite.headline": "Invite Friends", | 275 | "settings.invite.headline": "邀请好å‹", |
274 | "settings.navigation.account": "Account", | 276 | "settings.navigation.account": "账户", |
275 | "settings.navigation.availableServices": "Available services", | 277 | "settings.navigation.availableServices": "å¯ç”¨çš„æœåŠ¡", |
276 | "settings.navigation.logout": "Logout", | 278 | "settings.navigation.logout": "注销", |
277 | "settings.navigation.supportFerdi": "About Ferdi", | 279 | "settings.navigation.supportFerdi": "关于 Ferdi", |
278 | "settings.navigation.team": "Manage Team", | 280 | "settings.navigation.team": "Manage Team", |
279 | "settings.navigation.yourServices": "Your services", | 281 | "settings.navigation.yourServices": "您的æœåŠ¡", |
280 | "settings.navigation.yourWorkspaces": "Your workspaces", | 282 | "settings.navigation.yourWorkspaces": "您的工作区", |
281 | "settings.recipes.all": "All services", | 283 | "settings.recipes.all": "所有æœåŠ¡", |
282 | "settings.recipes.custom": "Custom Services", | 284 | "settings.recipes.custom": "自定义æœåŠ¡", |
283 | "settings.recipes.customService.headline.communityRecipes": "Community 3rd Party Recipes", | 285 | "settings.recipes.customService.headline.communityRecipes": "Community 3rd Party Recipes", |
284 | "settings.recipes.customService.headline.customRecipes": "Custom 3rd Party Recipes", | 286 | "settings.recipes.customService.headline.customRecipes": "Custom 3rd Party Recipes", |
285 | "settings.recipes.customService.headline.devRecipes": "Your Development Service Recipes", | 287 | "settings.recipes.customService.headline.devRecipes": "Your Development Service Recipes", |
286 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", | 288 | "settings.recipes.customService.intro": "To add a custom service, copy the service recipe to:", |
287 | "settings.recipes.customService.openDevDocs": "Developer Documentation", | 289 | "settings.recipes.customService.openDevDocs": "å¼€å‘者文档", |
288 | "settings.recipes.customService.openFolder": "Open folder", | 290 | "settings.recipes.customService.openFolder": "打开文件夹", |
289 | "settings.recipes.headline": "Available services", | 291 | "settings.recipes.headline": "å¯ç”¨çš„æœåŠ¡", |
290 | "settings.recipes.missingService": "Missing a service?", | 292 | "settings.recipes.missingService": "找ä¸åˆ°æœåŠ¡ï¼Ÿ", |
291 | "settings.recipes.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 293 | "settings.recipes.nothingFound": "对ä¸èµ·ï¼Œæ²¡æœ‰ä»»ä½•æœåŠ¡åŒ¹é…您的æœç´¢è¯ - 但您ä»ç„¶å¯ä»¥ä½¿ç”¨â€œè‡ªå®šä¹‰ç½‘ç«™â€é€‰é¡¹æ·»åŠ 它。 请注æ„,网站å¯èƒ½ä¼šæ˜¾ç¤ºè‡ªæ‚¨å½“å‰ä½¿ç”¨çš„版本以æ¥æ·»åŠ 到Ferdi的更多æœåŠ¡ã€‚ 为了获得这些新æœåŠ¡ï¼Œè¯·è€ƒè™‘å‡çº§ä¸ºè¾ƒæ–°ç‰ˆæœ¬çš„费尔迪。", |
292 | "settings.recipes.servicesSuccessfulAddedInfo": "Service successfully added", | 294 | "settings.recipes.servicesSuccessfulAddedInfo": "æœåŠ¡æ·»åŠ æˆåŠŸ", |
293 | "settings.searchService": "Search service", | 295 | "settings.searchService": "æœç´¢æœåŠ¡", |
294 | "settings.service.error.goBack": "Back to services", | 296 | "settings.service.error.goBack": "返回æœåŠ¡åˆ—表", |
295 | "settings.service.error.headline": "Error", | 297 | "settings.service.error.headline": "错误", |
296 | "settings.service.error.message": "Could not load service recipe.", | 298 | "settings.service.error.message": "Could not load service recipe.", |
297 | "settings.service.form.addServiceHeadline": "Add {name}", | 299 | "settings.service.form.addServiceHeadline": "æ·»åŠ {name}", |
298 | "settings.service.form.availableServices": "Available services", | 300 | "settings.service.form.availableServices": "å¯ç”¨çš„æœåŠ¡", |
299 | "settings.service.form.customUrl": "Custom server", | 301 | "settings.service.form.customUrl": "自定义æœåŠ¡å™¨", |
300 | "settings.service.form.customUrlValidationError": "Could not validate custom {name} server.", | 302 | "settings.service.form.customUrlValidationError": "æ— æ³•éªŒè¯è‡ªå®šä¹‰ {name} æœåŠ¡å™¨ã€‚", |
301 | "settings.service.form.darkReaderBrightness": "Dark Reader Brightness", | 303 | "settings.service.form.darkReaderBrightness": "Dark Reader Brightness", |
302 | "settings.service.form.darkReaderContrast": "Dark Reader Contrast", | 304 | "settings.service.form.darkReaderContrast": "Dark Reader Contrast", |
303 | "settings.service.form.darkReaderSepia": "Dark Reader Sepia", | 305 | "settings.service.form.darkReaderSepia": "Dark Reader Sepia", |
304 | "settings.service.form.deleteButton": "Delete service", | 306 | "settings.service.form.deleteButton": "åˆ é™¤æœåŠ¡", |
305 | "settings.service.form.editServiceHeadline": "Edit {name}", | 307 | "settings.service.form.editServiceHeadline": "编辑 {name}", |
306 | "settings.service.form.enableAudio": "Enable audio", | 308 | "settings.service.form.enableAudio": "å¯ç”¨éŸ³é¢‘", |
307 | "settings.service.form.enableBadge": "Show unread message badges", | 309 | "settings.service.form.enableBadge": "Show unread message badges", |
308 | "settings.service.form.enableDarkMode": "Enable Dark Mode", | 310 | "settings.service.form.enableDarkMode": "å¼€å¯æ·±è‰²ä¸»é¢˜", |
309 | "settings.service.form.enableHibernation": "Enable hibernation", | 311 | "settings.service.form.enableHibernation": "å¯ç”¨ä¼‘çœ ", |
310 | "settings.service.form.enableNotification": "Enable notifications", | 312 | "settings.service.form.enableNotification": "å¼€å¯é€šçŸ¥", |
311 | "settings.service.form.enableService": "Enable service", | 313 | "settings.service.form.enableService": "å¯ç”¨æœåŠ¡", |
314 | "settings.service.form.enableWakeUp": "Enable wake up", | ||
312 | "settings.service.form.headlineBadges": "Unread message badges", | 315 | "settings.service.form.headlineBadges": "Unread message badges", |
313 | "settings.service.form.headlineDarkReaderSettings": "Dark Reader Settings", | 316 | "settings.service.form.headlineDarkReaderSettings": "暗色阅读器设置", |
314 | "settings.service.form.headlineGeneral": "General", | 317 | "settings.service.form.headlineGeneral": "一般信æ¯", |
315 | "settings.service.form.headlineNotifications": "Notifications", | 318 | "settings.service.form.headlineNotifications": "通知消æ¯", |
316 | "settings.service.form.icon": "Custom icon", | 319 | "settings.service.form.icon": "è‡ªå®šä¹‰å›¾æ ‡", |
317 | "settings.service.form.iconDelete": "åˆ é™¤", | 320 | "settings.service.form.iconDelete": "åˆ é™¤", |
318 | "settings.service.form.iconUpload": "Drop your image, or click here", | 321 | "settings.service.form.iconUpload": "拖入图åƒæˆ–点击这里", |
319 | "settings.service.form.indirectMessageInfo": "You will be notified about all new messages in a channel, not just @username, @channel, @here, ...", | 322 | "settings.service.form.indirectMessageInfo": "您将收到一个频é“ä¸æ‰€æœ‰æ–°æ¶ˆæ¯çš„通知,而ä¸ä»…仅是@username, @channel, @here...", |
320 | "settings.service.form.indirectMessages": "Show message badge for all new messages", | 323 | "settings.service.form.indirectMessages": "Show message badge for all new messages", |
321 | "settings.service.form.isHibernatedEnabledInfo": "When enabled, a service will be shut down after a period of time to save system resources.", | 324 | "settings.service.form.isHibernatedEnabledInfo": "å¯ç”¨æ—¶ï¼ŒæœåŠ¡å°†åœ¨ä¸€æ®µæ—¶é—´åŽå…³é—,以节çœç³»ç»Ÿèµ„æºã€‚", |
322 | "settings.service.form.isMutedInfo": "When disabled, all notification sounds and audio playback are muted", | 325 | "settings.service.form.isMutedInfo": "ç¦ç”¨æ—¶ï¼Œæ‰€æœ‰é€šçŸ¥å£°éŸ³å’ŒéŸ³é¢‘将被é™éŸ³", |
323 | "settings.service.form.name": "å称", | 326 | "settings.service.form.name": "å称", |
324 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", | 327 | "settings.service.form.onlyShowFavoritesInUnreadCount": "Only show Favorites in unread count", |
325 | "settings.service.form.openDarkmodeCss": "Open darkmode.css", | 328 | "settings.service.form.openDarkmodeCss": "打开 darkmode.css", |
326 | "settings.service.form.openUserCss": "Open user.css", | 329 | "settings.service.form.openUserCss": "打开 user.css", |
327 | "settings.service.form.openUserJs": "Open user.js", | 330 | "settings.service.form.openUserJs": "打开user.js", |
328 | "settings.service.form.proxy.headline": "HTTP/HTTPS Proxy Settings", | 331 | "settings.service.form.proxy.headline": "HTTP/HTTPS 代ç†è®¾ç½®", |
329 | "settings.service.form.proxy.host": "Proxy Host/IP", | 332 | "settings.service.form.proxy.host": "代ç†ä¸»æœº/IP", |
330 | "settings.service.form.proxy.info": "Proxy settings will not be synchronized with the Ferdi servers.", | 333 | "settings.service.form.proxy.info": "代ç†è®¾ç½®å°†ä¸ä¼šä¸Ž Ferdi æœåŠ¡å™¨åŒæ¥ã€‚", |
331 | "settings.service.form.proxy.isEnabled": "Use Proxy", | 334 | "settings.service.form.proxy.isEnabled": "使用代ç†", |
332 | "settings.service.form.proxy.password": "Password (optional)", | 335 | "settings.service.form.proxy.password": "密ç (å¯é€‰)", |
333 | "settings.service.form.proxy.port": "Port", | 336 | "settings.service.form.proxy.port": "端å£", |
334 | "settings.service.form.proxy.restartInfo": "Please restart Ferdi after changing proxy Settings.", | 337 | "settings.service.form.proxy.restartInfo": "更改代ç†è®¾ç½®åŽï¼Œè¯·é‡å¯ Ferdi。", |
335 | "settings.service.form.proxy.user": "User (optional)", | 338 | "settings.service.form.proxy.user": "用户 (å¯é€‰)", |
336 | "settings.service.form.recipeFileInfo": "Your user files will be inserted into the webpage so you can customize services in any way you like. User files are only stored locally and are not transferred to other computers using the same account.", | 339 | "settings.service.form.recipeFileInfo": "您的用户文件将被æ’入网页,以便您å¯ä»¥ä»¥ä»»ä½•æ–¹å¼è‡ªå®šä¹‰æœåŠ¡ã€‚ 用户文件åªå˜å‚¨åœ¨æœ¬åœ°ï¼Œä¸ä¼šè¢«åŒä¸€è´¦æˆ·åŒæ¥åˆ°å…¶ä»–计算机。", |
337 | "settings.service.form.saveButton": "Save service", | 340 | "settings.service.form.saveButton": "ä¿å˜æœåŠ¡", |
338 | "settings.service.form.tabHosted": "Hosted", | 341 | "settings.service.form.tabHosted": "托管的", |
339 | "settings.service.form.tabOnPremise": "Self hosted âï¸", | 342 | "settings.service.form.tabOnPremise": "自我托管 âï¸", |
340 | "settings.service.form.team": "Team", | 343 | "settings.service.form.team": "团队", |
341 | "settings.service.form.useHostedService": "Use the hosted {name} service.", | 344 | "settings.service.form.useHostedService": "使用托管的 {name} æœåŠ¡ã€‚", |
342 | "settings.service.form.yourServices": "Your services", | 345 | "settings.service.form.yourServices": "您的æœåŠ¡", |
343 | "settings.services.deletedInfo": "Service has been deleted", | 346 | "settings.services.deletedInfo": "æœåŠ¡å·²è¢«åˆ 除", |
344 | "settings.services.discoverServices": "Discover services", | 347 | "settings.services.discoverServices": "å‘现æœåŠ¡", |
345 | "settings.services.headline": "Your services", | 348 | "settings.services.headline": "您的æœåŠ¡", |
346 | "settings.services.noServicesAdded": "Start by adding a service.", | 349 | "settings.services.noServicesAdded": "ä»Žæ·»åŠ æœåŠ¡å¼€å§‹ã€‚", |
347 | "settings.services.nothingFound": "Sorry, but no service matched your search term - but you can still probably add it using the \"Custom Website\" option. Please note that the website might show more services that have been added to Ferdi since the version that you are currently on. To get those new services, please consider upgrading to a newer version of Ferdi.", | 350 | "settings.services.nothingFound": "对ä¸èµ·ï¼Œæ²¡æœ‰ä»»ä½•æœåŠ¡åŒ¹é…您的æœç´¢è¯ - 但您ä»ç„¶å¯ä»¥ä½¿ç”¨â€œè‡ªå®šä¹‰ç½‘ç«™â€é€‰é¡¹æ·»åŠ 它。 请注æ„,网站å¯èƒ½ä¼šæ˜¾ç¤ºè‡ªæ‚¨å½“å‰ä½¿ç”¨çš„版本以æ¥æ·»åŠ 到Ferdi的更多æœåŠ¡ã€‚ 为了获得这些新æœåŠ¡ï¼Œè¯·è€ƒè™‘å‡çº§ä¸ºè¾ƒæ–°ç‰ˆæœ¬çš„费尔迪。", |
348 | "settings.services.servicesRequestFailed": "Could not load your services", | 351 | "settings.services.servicesRequestFailed": "æ— æ³•åŠ è½½æ‚¨çš„æœåŠ¡", |
349 | "settings.services.tooltip.isDisabled": "Service is disabled", | 352 | "settings.services.tooltip.isDisabled": "æœåŠ¡å·²ç¦ç”¨", |
350 | "settings.services.tooltip.isMuted": "All sounds are muted", | 353 | "settings.services.tooltip.isMuted": "所有声音被é™éŸ³", |
351 | "settings.services.tooltip.notificationsDisabled": "Notifications are disabled", | 354 | "settings.services.tooltip.notificationsDisabled": "通知已ç¦ç”¨", |
352 | "settings.services.updatedInfo": "Your changes have been saved", | 355 | "settings.services.updatedInfo": "您的更改已ä¿å˜", |
353 | "settings.supportFerdi.aboutIntro": "<p>Ferdi is an open-source and a community-lead application.</p><p>Thanks to the people who make this possbile:</p>", | 356 | "settings.supportFerdi.aboutIntro": "<p>Ferdi是一个开æºçš„软件。</p><p>感谢那些使Ferdiæˆä¸ºå¯èƒ½çš„人:</p>", |
354 | "settings.supportFerdi.bannerText": "Do you want to help us improve Ferdi?", | 357 | "settings.supportFerdi.bannerText": "ä½ æƒ³è¦å¸®åŠ©æˆ‘们改进Ferdiå—?", |
355 | "settings.supportFerdi.headline": "About Ferdi", | 358 | "settings.supportFerdi.headline": "关于 Ferdi", |
356 | "settings.supportFerdi.openSurvey": "Open survey", | 359 | "settings.supportFerdi.openSurvey": "打开调查", |
357 | "settings.supportFerdi.textDonation": "If you feel like supporting Ferdi development with a donation, you can do so on both,", | 360 | "settings.supportFerdi.textDonation": "å¦‚æžœä½ æƒ³è¦æèµ Ferdiçš„å¼€å‘ï¼Œä½ å¯ä»¥é€šè¿‡ï¼š", |
358 | "settings.supportFerdi.textDonationAnd": "and", | 361 | "settings.supportFerdi.textDonationAnd": "和", |
359 | "settings.supportFerdi.textExpenses": "While volunteers do most of the work, we still need to pay for servers and certificates. As a community, we are fully transparent on funds we collect and spend - see our", | 362 | "settings.supportFerdi.textExpenses": "虽然是志愿者åšå¤§éƒ¨åˆ†å·¥ä½œï¼Œä½†æˆ‘们ä»ç„¶éœ€è¦ç”¨é’±æ¥è´ä¹°æœåŠ¡å™¨å’Œè¯ä¹¦ã€‚ 作为一个社区应用,我们在募集和花费的资金上是完全公开的—— 看看我们的", |
360 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", | 363 | "settings.supportFerdi.textGitHubSponsors": "GitHub Sponsors", |
361 | "settings.supportFerdi.textListContributors": "Full list of contributors", | 364 | "settings.supportFerdi.textListContributors": "贡献者列表", |
362 | "settings.supportFerdi.textListContributorsHere": "here", | 365 | "settings.supportFerdi.textListContributorsHere": "这里", |
363 | "settings.supportFerdi.textOpenCollective": "Open Collective", | 366 | "settings.supportFerdi.textOpenCollective": "Open Collective", |
364 | "settings.supportFerdi.textSupportWelcome": "Support is always welcome. You can find a list of the help we need", | 367 | "settings.supportFerdi.textSupportWelcome": "支æŒæ€»æ˜¯å—欢迎的。您å¯ä»¥æ‰¾åˆ°æˆ‘们需è¦çš„帮助列表", |
365 | "settings.supportFerdi.textSupportWelcomeHere": "here", | 368 | "settings.supportFerdi.textSupportWelcomeHere": "这里", |
366 | "settings.supportFerdi.textVolunteers": "The development of Ferdi is done by volunteers. People who use Ferdi like you. They maintain, fix, and improve Ferdi in their spare time.", | 369 | "settings.supportFerdi.textVolunteers": "Ferdi是由志愿者开å‘而æˆã€‚他们用空闲时间维护ã€ä¿®å¤å’Œæ”¹è¿›Ferdi。", |
367 | "settings.supportFerdi.title": "Do you like Ferdi?", | 370 | "settings.supportFerdi.title": "ä½ å–œæ¬¢Ferdiå—?", |
368 | "settings.team.contentHeadline": "Franz Team Management", | 371 | "settings.team.contentHeadline": "Franz Team Management", |
369 | "settings.team.copy": "Franz's Team Management allows you to manage Franz Subscriptions for multiple users. Please keep in mind that having a Franz Premium subscription will give you no advantages in using Ferdi: The only reason you still have access to Team Management is so you can manage your legacy Franz Teams and so that you don't loose any functionality in managing your account.", | 372 | "settings.team.copy": "Franz's Team Management allows you to manage Franz Subscriptions for multiple users. Please keep in mind that having a Franz Premium subscription will give you no advantages in using Ferdi: The only reason you still have access to Team Management is so you can manage your legacy Franz Teams and so that you don't loose any functionality in managing your account.", |
370 | "settings.team.headline": "Team", | 373 | "settings.team.headline": "团队", |
371 | "settings.team.intro": "You are currently using Franz Servers, which is why you have access to Team Management.", | 374 | "settings.team.intro": "You are currently using Franz Servers, which is why you have access to Team Management.", |
372 | "settings.team.manageAction": "Manage your Team on meetfranz.com", | 375 | "settings.team.manageAction": "Manage your Team on meetfranz.com", |
373 | "settings.team.teamsUnavailable": "Teams are unavailable", | 376 | "settings.team.teamsUnavailable": "Teams are unavailable", |
374 | "settings.team.teamsUnavailableInfo": "Teams are currently only available when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.", | 377 | "settings.team.teamsUnavailableInfo": "Teams are currently only available when using the Franz Server and after paying for Franz Professional. Please change your server to https://api.franzinfra.com to use teams.", |
375 | "settings.user.form.accountType.company": "Company", | 378 | "settings.user.form.accountType.company": "å…¬å¸", |
376 | "settings.user.form.accountType.individual": "Individual", | 379 | "settings.user.form.accountType.individual": "个人", |
377 | "settings.user.form.accountType.label": "Account type", | 380 | "settings.user.form.accountType.label": "è´¦å·ç±»åž‹", |
378 | "settings.user.form.accountType.non-profit": "Non-Profit", | 381 | "settings.user.form.accountType.non-profit": "éžç›ˆåˆ©çš„", |
379 | "settings.user.form.currentPassword": "Current password", | 382 | "settings.user.form.currentPassword": "当å‰å¯†ç ", |
380 | "settings.user.form.email": "Email", | 383 | "settings.user.form.email": "Email", |
381 | "settings.user.form.firstname": "First Name", | 384 | "settings.user.form.firstname": "åå—", |
382 | "settings.user.form.lastname": "Last Name", | 385 | "settings.user.form.lastname": "姓æ°", |
383 | "settings.user.form.newPassword": "New password", | 386 | "settings.user.form.newPassword": "新密ç ", |
384 | "settings.workspace.add.form.name": "å称", | 387 | "settings.workspace.add.form.name": "å称", |
385 | "settings.workspace.add.form.submitButton": "Create workspace", | 388 | "settings.workspace.add.form.submitButton": "创建工作区", |
386 | "settings.workspace.form.buttonDelete": "Delete workspace", | 389 | "settings.workspace.form.buttonDelete": "åˆ é™¤å·¥ä½œåŒº", |
387 | "settings.workspace.form.buttonSave": "Save workspace", | 390 | "settings.workspace.form.buttonSave": "ä¿å˜å·¥ä½œåŒº", |
388 | "settings.workspace.form.keepLoaded": "Keep this workspace loaded*", | 391 | "settings.workspace.form.keepLoaded": "ä¿æŒæ¤å·¥ä½œåŒºåŠ è½½*", |
389 | "settings.workspace.form.keepLoadedInfo": "*This option will be overwritten by the global \"Keep all workspaces loaded\" option.", | 392 | "settings.workspace.form.keepLoadedInfo": "*æ¤é€‰é¡¹å°†è¢«å…¨å±€é€‰é¡¹â€œä¿æŒæ‰€æœ‰å·¥ä½œåŒºåŠ è½½â€æ‰€è¦†ç›–。", |
390 | "settings.workspace.form.name": "å称", | 393 | "settings.workspace.form.name": "å称", |
391 | "settings.workspace.form.servicesInWorkspaceHeadline": "Services in this Workspace", | 394 | "settings.workspace.form.servicesInWorkspaceHeadline": "æ¤å·¥ä½œåŒºä¸çš„æœåŠ¡", |
392 | "settings.workspace.form.yourWorkspaces": "Your workspaces", | 395 | "settings.workspace.form.yourWorkspaces": "您的工作区", |
393 | "settings.workspaces.deletedInfo": "Workspace has been deleted", | 396 | "settings.workspaces.deletedInfo": "å·¥ä½œåŒºå·²è¢«åˆ é™¤", |
394 | "settings.workspaces.headline": "Your workspaces", | 397 | "settings.workspaces.headline": "您的工作区", |
395 | "settings.workspaces.noWorkspacesAdded": "You haven't created any workspaces yet.", | 398 | "settings.workspaces.noWorkspacesAdded": "您尚未创建任何工作区。", |
396 | "settings.workspaces.tryReloadWorkspaces": "Try again", | 399 | "settings.workspaces.tryReloadWorkspaces": "é‡æ–°å°è¯•", |
397 | "settings.workspaces.updatedInfo": "Your changes have been saved", | 400 | "settings.workspaces.updatedInfo": "您的更改已ä¿å˜", |
398 | "settings.workspaces.workspaceFeatureHeadline": "Less is More: Introducing Ferdi Workspaces", | 401 | "settings.workspaces.workspaceFeatureHeadline": "Less is More: Introducing Ferdi Workspaces", |
399 | "settings.workspaces.workspaceFeatureInfo": "Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time. You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.", | 402 | "settings.workspaces.workspaceFeatureInfo": "Ferdi工作区å¯ä»¥è®©æ‚¨ä¸“注于当下最é‡è¦çš„事情。 设置ä¸åŒçš„æœåŠ¡é›†åˆï¼Œè®©æ‚¨éšæ—¶å¯ä»¥çš„在它们之间轻æ¾çš„切æ¢ã€‚ ", |
400 | "settings.workspaces.workspacesRequestFailed": "Could not load your workspaces", | 403 | "settings.workspaces.workspacesRequestFailed": "æ— æ³•åŠ è½½æ‚¨çš„å·¥ä½œåŒº", |
401 | "setupAssistant.headline": "Let's get started", | 404 | "setupAssistant.headline": "我们开始å§ï¼", |
402 | "setupAssistant.subheadline": "Choose from our most used services and get back on top of your messaging now.", | 405 | "setupAssistant.subheadline": "Choose from our most used services and get back on top of your messaging now.", |
403 | "setupAssistant.submit.label": "Let's go", | 406 | "setupAssistant.submit.label": "让我们开始å§", |
404 | "sidebar.addNewService": "Add new service", | 407 | "sidebar.addNewService": "æ·»åŠ æœåŠ¡", |
405 | "sidebar.closeTodosDrawer": "Close Ferdi Todos", | 408 | "sidebar.closeTodosDrawer": "å…³é—Ferdi Todos", |
406 | "sidebar.closeWorkspaceDrawer": "Close workspace drawer", | 409 | "sidebar.closeWorkspaceDrawer": "å…³é—工作区抽屉æ ", |
407 | "sidebar.lockFerdi": "Lock Ferdi", | 410 | "sidebar.lockFerdi": "é”定Ferdi", |
408 | "sidebar.muteApp": "Disable notifications & audio", | 411 | "sidebar.muteApp": "ç¦ç”¨é€šçŸ¥å’ŒéŸ³é¢‘", |
409 | "sidebar.openTodosDrawer": "Open Ferdi Todos", | 412 | "sidebar.openTodosDrawer": "å¼€å¯Ferdi Todos", |
410 | "sidebar.openWorkspaceDrawer": "Open workspace drawer", | 413 | "sidebar.openWorkspaceDrawer": "打开工作区抽屉æ ", |
411 | "sidebar.unmuteApp": "Enable notifications & audio", | 414 | "sidebar.unmuteApp": "å¯ç”¨é€šçŸ¥å’ŒéŸ³é¢‘", |
412 | "signup.email.label": "电å邮箱地å€", | 415 | "signup.email.label": "电å邮箱地å€", |
413 | "signup.emailDuplicate": "A user with that email address already exists", | 416 | "signup.emailDuplicate": "该电å邮件地å€çš„用户已ç»å˜åœ¨", |
414 | "signup.firstname.label": "First Name", | 417 | "signup.firstname.label": "åå—", |
415 | "signup.headline": "Sign up", | 418 | "signup.headline": "登录", |
416 | "signup.lastname.label": "Last Name", | 419 | "signup.lastname.label": "姓æ°", |
417 | "signup.legal.info": "By creating a Ferdi account you accept the", | 420 | "signup.legal.info": "创建å¸æˆ·åˆ™è¡¨æ˜Žæ‚¨æŽ¥å—我们的", |
418 | "signup.legal.privacy": "Privacy Statement", | 421 | "signup.legal.privacy": "éšç§å£°æ˜Ž", |
419 | "signup.legal.terms": "Terms of service", | 422 | "signup.legal.terms": "æœåŠ¡æ¡æ¬¾", |
420 | "signup.link.login": "Already have an account, sign in?", | 423 | "signup.link.login": "已有账户?请登录", |
421 | "signup.password.label": "Password", | 424 | "signup.password.label": "密ç ", |
422 | "signup.submit.label": "Create account", | 425 | "signup.submit.label": "创建账户", |
423 | "tabs.item.confirmDeleteService": "Do you really want to delete the {serviceName} service?", | 426 | "tabs.item.confirmDeleteService": "ä½ çœŸçš„è¦åˆ 除 {serviceName} æœåŠ¡ä¹ˆï¼Ÿ", |
424 | "tabs.item.deleteService": "Delete service", | 427 | "tabs.item.deleteService": "åˆ é™¤æœåŠ¡", |
425 | "tabs.item.disableAudio": "Disable audio", | 428 | "tabs.item.disableAudio": "ç¦ç”¨éŸ³é¢‘", |
426 | "tabs.item.disableDarkMode": "Disable Dark mode", | 429 | "tabs.item.disableDarkMode": "ç¦ç”¨æš—色模å¼", |
427 | "tabs.item.disableNotifications": "Disable notifications", | 430 | "tabs.item.disableNotifications": "ç¦ç”¨é€šçŸ¥", |
428 | "tabs.item.disableService": "Disable service", | 431 | "tabs.item.disableService": "ç¦ç”¨æœåŠ¡", |
429 | "tabs.item.enableAudio": "Enable audio", | 432 | "tabs.item.enableAudio": "å¯ç”¨éŸ³é¢‘", |
430 | "tabs.item.enableDarkMode": "Enable Dark mode", | 433 | "tabs.item.enableDarkMode": "å¼€å¯æ·±è‰²ä¸»é¢˜", |
431 | "tabs.item.enableNotification": "Enable notifications", | 434 | "tabs.item.enableNotification": "å¼€å¯é€šçŸ¥", |
432 | "tabs.item.enableService": "Enable service", | 435 | "tabs.item.enableService": "å¯ç”¨æœåŠ¡", |
433 | "tabs.item.hibernateService": "Hibernate service", | 436 | "tabs.item.hibernateService": "ä¼‘çœ æœåŠ¡", |
434 | "tabs.item.reload": "é‡æ–°åŠ è½½", | 437 | "tabs.item.reload": "é‡æ–°åŠ è½½", |
435 | "tabs.item.wakeUpService": "Wake up service", | 438 | "tabs.item.wakeUpService": "唤醒æœåŠ¡", |
436 | "validation.email": "{field} is not valid", | 439 | "validation.email": "{field} æ— æ•ˆ", |
437 | "validation.minLength": "{field} should be at least {length} characters long", | 440 | "validation.minLength": "{field} 的长度应该大于 {length} 个å—符", |
438 | "validation.oneRequired": "At least one is required", | 441 | "validation.oneRequired": "至少需è¦ä¸€ä¸ª", |
439 | "validation.required": "{field} is required", | 442 | "validation.required": "{field} 为必填å—段", |
440 | "validation.url": "{field} is not a valid URL", | 443 | "validation.url": "{field} ä¸æ˜¯ä¸€ä¸ªæœ‰æ•ˆçš„ URL", |
441 | "webControls.back": "返回", | 444 | "webControls.back": "返回", |
442 | "webControls.forward": "Forward", | 445 | "webControls.forward": "å‘å‰", |
443 | "webControls.goHome": "主页", | 446 | "webControls.goHome": "主页", |
444 | "webControls.openInBrowser": "Open in Browser", | 447 | "webControls.openInBrowser": "在æµè§ˆå™¨ä¸æ‰“å¼€", |
445 | "webControls.reload": "é‡æ–°åŠ è½½", | 448 | "webControls.reload": "é‡æ–°åŠ è½½", |
446 | "welcome.loginButton": "Login to your account", | 449 | "welcome.loginButton": "登录您的账å·", |
447 | "welcome.signupButton": "Create a free account", | 450 | "welcome.signupButton": "创建å…费账户", |
448 | "workspaceDrawer.addNewWorkspaceLabel": "Add new workspace", | 451 | "workspaceDrawer.addNewWorkspaceLabel": "新建工作区…", |
449 | "workspaceDrawer.allServices": "All services", | 452 | "workspaceDrawer.allServices": "所有æœåŠ¡", |
450 | "workspaceDrawer.headline": "Workspaces", | 453 | "workspaceDrawer.headline": "工作区", |
451 | "workspaceDrawer.item.contextMenuEdit": "edit", | 454 | "workspaceDrawer.item.contextMenuEdit": "编辑", |
452 | "workspaceDrawer.item.noServicesAddedYet": "No services added yet", | 455 | "workspaceDrawer.item.noServicesAddedYet": "å°šæœªæ·»åŠ ä»»ä½•æœåŠ¡", |
453 | "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", | 456 | "workspaceDrawer.workspaceFeatureInfo": "<p>Ferdi Workspaces let you focus on what’s important right now. Set up different sets of services and easily switch between them at any time.</p><p>You decide which services you need when and where, so we can help you stay on top of your game - or easily switch off from work whenever you want.</p>", |
454 | "workspaceDrawer.workspacesSettingsTooltip": "Edit workspaces settings", | 457 | "workspaceDrawer.workspacesSettingsTooltip": "工作区设置", |
455 | "workspaces.switchingIndicator.switchingTo": "Switching to" | 458 | "workspaces.switchingIndicator.switchingTo": "切æ¢åˆ°" |
456 | } | 459 | } |
diff --git a/src/index.js b/src/index.ts index 9f384f5fa..b4a754289 100644 --- a/src/index.js +++ b/src/index.ts | |||
@@ -1,6 +1,13 @@ | |||
1 | /* eslint-disable import/first */ | 1 | /* eslint-disable import/first */ |
2 | 2 | ||
3 | import { app, BrowserWindow, globalShortcut, ipcMain, session, dialog } from 'electron'; | 3 | import { |
4 | app, | ||
5 | BrowserWindow, | ||
6 | globalShortcut, | ||
7 | ipcMain, | ||
8 | session, | ||
9 | dialog, | ||
10 | } from 'electron'; | ||
4 | 11 | ||
5 | import { emptyDirSync, ensureFileSync } from 'fs-extra'; | 12 | import { emptyDirSync, ensureFileSync } from 'fs-extra'; |
6 | import { join } from 'path'; | 13 | import { join } from 'path'; |
@@ -13,12 +20,7 @@ initializeRemote(); | |||
13 | 20 | ||
14 | import { DEFAULT_APP_SETTINGS, DEFAULT_WINDOW_OPTIONS } from './config'; | 21 | import { DEFAULT_APP_SETTINGS, DEFAULT_WINDOW_OPTIONS } from './config'; |
15 | 22 | ||
16 | import { | 23 | import { isMac, isWindows, isLinux, altKey } from './environment'; |
17 | isMac, | ||
18 | isWindows, | ||
19 | isLinux, | ||
20 | altKey, | ||
21 | } from './environment'; | ||
22 | import { | 24 | import { |
23 | isDevMode, | 25 | isDevMode, |
24 | aboutAppDetails, | 26 | aboutAppDetails, |
@@ -34,7 +36,8 @@ import DBus from './lib/DBus'; | |||
34 | import Settings from './electron/Settings'; | 36 | import Settings from './electron/Settings'; |
35 | import handleDeepLink from './electron/deepLinking'; | 37 | import handleDeepLink from './electron/deepLinking'; |
36 | import { isPositionValid } from './electron/windowUtils'; | 38 | import { isPositionValid } from './electron/windowUtils'; |
37 | import { appId } from './package.json'; // eslint-disable-line import/no-unresolved | 39 | // @ts-expect-error Cannot find module './package.json' or its corresponding type declarations. |
40 | import { appId } from './package.json'; | ||
38 | import './electron/exception'; | 41 | import './electron/exception'; |
39 | 42 | ||
40 | import { asarPath } from './helpers/asar-helpers'; | 43 | import { asarPath } from './helpers/asar-helpers'; |
@@ -49,13 +52,18 @@ app.userAgentFallback = userAgent(); | |||
49 | 52 | ||
50 | // Keep a global reference of the window object, if you don't, the window will | 53 | // Keep a global reference of the window object, if you don't, the window will |
51 | // be closed automatically when the JavaScript object is garbage collected. | 54 | // be closed automatically when the JavaScript object is garbage collected. |
52 | let mainWindow; | 55 | let mainWindow: BrowserWindow | undefined; |
53 | let willQuitApp = false; | 56 | let willQuitApp = false; |
54 | 57 | ||
55 | // Register methods to be called once the window has been loaded. | 58 | // Register methods to be called once the window has been loaded. |
56 | let onDidLoadFns = []; | 59 | let onDidLoadFns: any[] | null = []; |
57 | 60 | ||
58 | function onDidLoad(fn) { | 61 | function onDidLoad(fn: { |
62 | (window: BrowserWindow): void; | ||
63 | (window: BrowserWindow): void; | ||
64 | (window: BrowserWindow): void; | ||
65 | (arg0: BrowserWindow): void; | ||
66 | }) { | ||
59 | if (onDidLoadFns) { | 67 | if (onDidLoadFns) { |
60 | onDidLoadFns.push(fn); | 68 | onDidLoadFns.push(fn); |
61 | } else if (mainWindow) { | 69 | } else if (mainWindow) { |
@@ -76,10 +84,10 @@ if (isWindows) { | |||
76 | const settings = new Settings('app', DEFAULT_APP_SETTINGS); | 84 | const settings = new Settings('app', DEFAULT_APP_SETTINGS); |
77 | const proxySettings = new Settings('proxy'); | 85 | const proxySettings = new Settings('proxy'); |
78 | 86 | ||
79 | const retrieveSettingValue = (key, defaultValue = true) => | 87 | const retrieveSettingValue = (key: string, defaultValue: boolean) => |
80 | ifUndefinedBoolean(settings.get(key), defaultValue); | 88 | ifUndefinedBoolean(settings.get(key), defaultValue); |
81 | 89 | ||
82 | if (retrieveSettingValue('sentry')) { | 90 | if (retrieveSettingValue('sentry', DEFAULT_APP_SETTINGS.sentry)) { |
83 | // eslint-disable-next-line global-require | 91 | // eslint-disable-next-line global-require |
84 | require('./sentry'); | 92 | require('./sentry'); |
85 | } | 93 | } |
@@ -96,7 +104,7 @@ const gotTheLock = liftSingleInstanceLock | |||
96 | if (!gotTheLock) { | 104 | if (!gotTheLock) { |
97 | app.quit(); | 105 | app.quit(); |
98 | } else { | 106 | } else { |
99 | app.on('second-instance', (event, argv) => { | 107 | app.on('second-instance', (_event, argv) => { |
100 | // Someone tried to run a second instance, we should focus our window. | 108 | // Someone tried to run a second instance, we should focus our window. |
101 | if (mainWindow) { | 109 | if (mainWindow) { |
102 | if (!mainWindow.isVisible()) { | 110 | if (!mainWindow.isVisible()) { |
@@ -108,7 +116,7 @@ if (!gotTheLock) { | |||
108 | mainWindow.focus(); | 116 | mainWindow.focus(); |
109 | 117 | ||
110 | if (isWindows) { | 118 | if (isWindows) { |
111 | onDidLoad(window => { | 119 | onDidLoad((window: BrowserWindow) => { |
112 | // Keep only command line / deep linked arguments | 120 | // Keep only command line / deep linked arguments |
113 | const url = argv.slice(1); | 121 | const url = argv.slice(1); |
114 | if (url) { | 122 | if (url) { |
@@ -145,6 +153,7 @@ if (!gotTheLock) { | |||
145 | // https://github.com/electron/electron/issues/9046 | 153 | // https://github.com/electron/electron/issues/9046 |
146 | if ( | 154 | if ( |
147 | isLinux && | 155 | isLinux && |
156 | process.env.XDG_CURRENT_DESKTOP && | ||
148 | ['Pantheon', 'Unity:Unity7'].includes(process.env.XDG_CURRENT_DESKTOP) | 157 | ['Pantheon', 'Unity:Unity7'].includes(process.env.XDG_CURRENT_DESKTOP) |
149 | ) { | 158 | ) { |
150 | process.env.XDG_CURRENT_DESKTOP = 'Unity'; | 159 | process.env.XDG_CURRENT_DESKTOP = 'Unity'; |
@@ -196,16 +205,20 @@ const createWindow = () => { | |||
196 | frame: isLinux, | 205 | frame: isLinux, |
197 | backgroundColor, | 206 | backgroundColor, |
198 | webPreferences: { | 207 | webPreferences: { |
199 | spellcheck: retrieveSettingValue('enableSpellchecking'), | 208 | spellcheck: retrieveSettingValue( |
209 | 'enableSpellchecking', | ||
210 | DEFAULT_APP_SETTINGS.enableSpellchecking, | ||
211 | ), | ||
200 | nodeIntegration: true, | 212 | nodeIntegration: true, |
201 | contextIsolation: false, | 213 | contextIsolation: false, |
202 | webviewTag: true, | 214 | webviewTag: true, |
203 | preload: join(__dirname, 'sentry.js'), | 215 | preload: join(__dirname, 'sentry.js'), |
216 | // @ts-expect-error Object literal may only specify known properties, and 'enableRemoteModule' does not exist in type 'WebPreferences'. | ||
204 | enableRemoteModule: true, | 217 | enableRemoteModule: true, |
205 | }, | 218 | }, |
206 | }); | 219 | }); |
207 | 220 | ||
208 | app.on('web-contents-created', (e, contents) => { | 221 | app.on('web-contents-created', (_e, contents) => { |
209 | if (contents.getType() === 'webview') { | 222 | if (contents.getType() === 'webview') { |
210 | contents.on('new-window', event => { | 223 | contents.on('new-window', event => { |
211 | event.preventDefault(); | 224 | event.preventDefault(); |
@@ -225,7 +238,7 @@ const createWindow = () => { | |||
225 | }); | 238 | }); |
226 | 239 | ||
227 | // Initialize System Tray | 240 | // Initialize System Tray |
228 | const trayIcon = new Tray(); | 241 | const trayIcon: Tray = new Tray(); |
229 | 242 | ||
230 | // Initialize DBus interface | 243 | // Initialize DBus interface |
231 | const dbus = new DBus(trayIcon); | 244 | const dbus = new DBus(trayIcon); |
@@ -256,7 +269,7 @@ const createWindow = () => { | |||
256 | 269 | ||
257 | // Windows deep linking handling on app launch | 270 | // Windows deep linking handling on app launch |
258 | if (isWindows) { | 271 | if (isWindows) { |
259 | onDidLoad(window => { | 272 | onDidLoad((window: BrowserWindow) => { |
260 | const url = process.argv.slice(1); | 273 | const url = process.argv.slice(1); |
261 | if (url) { | 274 | if (url) { |
262 | handleDeepLink(window, url.toString()); | 275 | handleDeepLink(window, url.toString()); |
@@ -270,24 +283,35 @@ const createWindow = () => { | |||
270 | // Dereference the window object, usually you would store windows | 283 | // Dereference the window object, usually you would store windows |
271 | // in an array if your app supports multi windows, this is the time | 284 | // in an array if your app supports multi windows, this is the time |
272 | // when you should delete the corresponding element. | 285 | // when you should delete the corresponding element. |
273 | if (!willQuitApp && retrieveSettingValue('runInBackground')) { | 286 | if ( |
287 | !willQuitApp && | ||
288 | retrieveSettingValue( | ||
289 | 'runInBackground', | ||
290 | DEFAULT_APP_SETTINGS.runInBackground, | ||
291 | ) | ||
292 | ) { | ||
274 | e.preventDefault(); | 293 | e.preventDefault(); |
275 | if (isWindows) { | 294 | if (isWindows) { |
276 | debug('Window: minimize'); | 295 | debug('Window: minimize'); |
277 | mainWindow.minimize(); | 296 | mainWindow?.minimize(); |
278 | 297 | ||
279 | if (retrieveSettingValue('closeToSystemTray')) { | 298 | if ( |
299 | retrieveSettingValue( | ||
300 | 'closeToSystemTray', | ||
301 | DEFAULT_APP_SETTINGS.closeToSystemTray, | ||
302 | ) | ||
303 | ) { | ||
280 | debug('Skip taskbar: true'); | 304 | debug('Skip taskbar: true'); |
281 | mainWindow.setSkipTaskbar(true); | 305 | mainWindow?.setSkipTaskbar(true); |
282 | } | 306 | } |
283 | } else if (isMac && mainWindow.isFullScreen()) { | 307 | } else if (isMac && mainWindow?.isFullScreen()) { |
284 | debug('Window: leaveFullScreen and hide'); | 308 | debug('Window: leaveFullScreen and hide'); |
285 | mainWindow.once('show', () => mainWindow.setFullScreen(true)); | 309 | mainWindow.once('show', () => mainWindow?.setFullScreen(true)); |
286 | mainWindow.once('leave-full-screen', () => mainWindow.hide()); | 310 | mainWindow.once('leave-full-screen', () => mainWindow?.hide()); |
287 | mainWindow.setFullScreen(false); | 311 | mainWindow.setFullScreen(false); |
288 | } else { | 312 | } else { |
289 | debug('Window: hide'); | 313 | debug('Window: hide'); |
290 | mainWindow.hide(); | 314 | mainWindow?.hide(); |
291 | } | 315 | } |
292 | } else { | 316 | } else { |
293 | dbus.stop(); | 317 | dbus.stop(); |
@@ -298,35 +322,49 @@ const createWindow = () => { | |||
298 | // For Windows we need to store a flag to properly restore the window | 322 | // For Windows we need to store a flag to properly restore the window |
299 | // if the window was maximized before minimizing it so system tray | 323 | // if the window was maximized before minimizing it so system tray |
300 | mainWindow.on('minimize', () => { | 324 | mainWindow.on('minimize', () => { |
325 | // @ts-expect-error Property 'wasMaximized' does not exist on type 'App'. | ||
301 | app.wasMaximized = app.isMaximized; | 326 | app.wasMaximized = app.isMaximized; |
302 | 327 | ||
303 | if (retrieveSettingValue('minimizeToSystemTray')) { | 328 | if ( |
329 | retrieveSettingValue( | ||
330 | 'minimizeToSystemTray', | ||
331 | DEFAULT_APP_SETTINGS.minimizeToSystemTray, | ||
332 | ) | ||
333 | ) { | ||
304 | debug('Skip taskbar: true'); | 334 | debug('Skip taskbar: true'); |
305 | mainWindow.setSkipTaskbar(true); | 335 | mainWindow?.setSkipTaskbar(true); |
306 | trayIcon.show(); | 336 | trayIcon.show(); |
307 | } | 337 | } |
308 | }); | 338 | }); |
309 | 339 | ||
310 | mainWindow.on('maximize', () => { | 340 | mainWindow.on('maximize', () => { |
311 | debug('Window: maximize'); | 341 | debug('Window: maximize'); |
342 | // @ts-expect-error Property 'isMaximized' does not exist on type 'App'. | ||
312 | app.isMaximized = true; | 343 | app.isMaximized = true; |
313 | }); | 344 | }); |
314 | 345 | ||
315 | mainWindow.on('unmaximize', () => { | 346 | mainWindow.on('unmaximize', () => { |
316 | debug('Window: unmaximize'); | 347 | debug('Window: unmaximize'); |
348 | // @ts-expect-error Property 'isMaximized' does not exist on type 'App'. | ||
317 | app.isMaximized = false; | 349 | app.isMaximized = false; |
318 | }); | 350 | }); |
319 | 351 | ||
320 | mainWindow.on('restore', () => { | 352 | mainWindow.on('restore', () => { |
321 | debug('Window: restore'); | 353 | debug('Window: restore'); |
322 | mainWindow.setSkipTaskbar(false); | 354 | mainWindow?.setSkipTaskbar(false); |
323 | 355 | ||
356 | // @ts-expect-error Property 'wasMaximized' does not exist on type 'App'. | ||
324 | if (app.wasMaximized) { | 357 | if (app.wasMaximized) { |
325 | debug('Window: was maximized before, maximize window'); | 358 | debug('Window: was maximized before, maximize window'); |
326 | mainWindow.maximize(); | 359 | mainWindow?.maximize(); |
327 | } | 360 | } |
328 | 361 | ||
329 | if (!retrieveSettingValue('enableSystemTray')) { | 362 | if ( |
363 | !retrieveSettingValue( | ||
364 | 'enableSystemTray', | ||
365 | DEFAULT_APP_SETTINGS.enableSystemTray, | ||
366 | ) | ||
367 | ) { | ||
330 | debug('Tray: hiding tray icon'); | 368 | debug('Tray: hiding tray icon'); |
331 | trayIcon.hide(); | 369 | trayIcon.hide(); |
332 | } | 370 | } |
@@ -340,10 +378,10 @@ const createWindow = () => { | |||
340 | 378 | ||
341 | mainWindow.on('show', () => { | 379 | mainWindow.on('show', () => { |
342 | debug('Skip taskbar: true'); | 380 | debug('Skip taskbar: true'); |
343 | mainWindow.setSkipTaskbar(false); | 381 | mainWindow?.setSkipTaskbar(false); |
344 | }); | 382 | }); |
345 | 383 | ||
346 | app.mainWindow = mainWindow; | 384 | // @ts-expect-error Property 'isMaximized' does not exist on type 'App'. |
347 | app.isMaximized = mainWindow.isMaximized(); | 385 | app.isMaximized = mainWindow.isMaximized(); |
348 | 386 | ||
349 | mainWindow.webContents.on('new-window', (e, url) => { | 387 | mainWindow.webContents.on('new-window', (e, url) => { |
@@ -351,17 +389,26 @@ const createWindow = () => { | |||
351 | openExternalUrl(url); | 389 | openExternalUrl(url); |
352 | }); | 390 | }); |
353 | 391 | ||
354 | if (retrieveSettingValue('startMinimized', false)) { | 392 | if ( |
393 | retrieveSettingValue('startMinimized', DEFAULT_APP_SETTINGS.startMinimized) | ||
394 | ) { | ||
355 | mainWindow.hide(); | 395 | mainWindow.hide(); |
356 | } else { | 396 | } else { |
357 | mainWindow.show(); | 397 | mainWindow.show(); |
358 | } | 398 | } |
359 | 399 | ||
360 | app.whenReady().then(() => { | 400 | app.whenReady().then(() => { |
361 | // Toggle the window on 'Alt+X' | 401 | if ( |
362 | globalShortcut.register(`${altKey()}+X`, () => { | 402 | retrieveSettingValue( |
363 | trayIcon.trayMenuTemplate[0].click(); | 403 | 'enableGlobalHideShortcut', |
364 | }); | 404 | DEFAULT_APP_SETTINGS.enableGlobalHideShortcut, |
405 | ) | ||
406 | ) { | ||
407 | // Toggle the window on 'Alt+X' | ||
408 | globalShortcut.register(`${altKey()}+X`, () => { | ||
409 | trayIcon.trayMenuTemplate[0].click(); | ||
410 | }); | ||
411 | } | ||
365 | }); | 412 | }); |
366 | }; | 413 | }; |
367 | 414 | ||
@@ -401,18 +448,18 @@ app.on('ready', () => { | |||
401 | 448 | ||
402 | // Register App URL | 449 | // Register App URL |
403 | const protocolClient = isDevMode ? 'ferdi-dev' : 'ferdi'; | 450 | const protocolClient = isDevMode ? 'ferdi-dev' : 'ferdi'; |
404 | if (!app.isDefaultProtocolClient(protocolClient)) { | 451 | if (!app.isDefaultProtocolClient(protocolClient, process.execPath)) { |
405 | app.setAsDefaultProtocolClient(protocolClient); | 452 | app.setAsDefaultProtocolClient(protocolClient, process.execPath); |
406 | } | 453 | } |
407 | 454 | ||
408 | if (isWindows) { | 455 | if (isWindows) { |
409 | const extraArgs = isDevMode ? `${__dirname} ` : ''; | 456 | const extraArgs = isDevMode ? `${__dirname} ` : ''; |
410 | const iconPath = asarPath( | 457 | const iconPath = asarPath( |
411 | join( | 458 | join( |
412 | isDevMode ? `${__dirname}../src/` : __dirname, | 459 | isDevMode ? `${__dirname}../src/` : __dirname, |
413 | 'assets/images/taskbar/win32/display.ico', | 460 | 'assets/images/taskbar/win32/display.ico', |
414 | ), | 461 | ), |
415 | ); | 462 | ); |
416 | app.setUserTasks([ | 463 | app.setUserTasks([ |
417 | { | 464 | { |
418 | program: process.execPath, | 465 | program: process.execPath, |
@@ -428,7 +475,7 @@ app.on('ready', () => { | |||
428 | iconPath, | 475 | iconPath, |
429 | iconIndex: 0, | 476 | iconIndex: 0, |
430 | title: 'Quit Ferdi', | 477 | title: 'Quit Ferdi', |
431 | description: null, | 478 | description: '', |
432 | }, | 479 | }, |
433 | ]); | 480 | ]); |
434 | } | 481 | } |
@@ -444,26 +491,28 @@ app.on('ready', () => { | |||
444 | const noop = () => null; | 491 | const noop = () => null; |
445 | let authCallback = noop; | 492 | let authCallback = noop; |
446 | 493 | ||
447 | app.on('login', (event, webContents, request, authInfo, callback) => { | 494 | app.on('login', (event, _webContents, _request, authInfo, callback) => { |
495 | // @ts-expect-error Type '(username?: string | undefined, password?: string | undefined) => void' is not assignable to type '() => null'. | ||
448 | authCallback = callback; | 496 | authCallback = callback; |
449 | debug('browser login event', authInfo); | 497 | debug('browser login event', authInfo); |
450 | event.preventDefault(); | 498 | event.preventDefault(); |
451 | 499 | ||
452 | if (!authInfo.isProxy && authInfo.scheme === 'basic') { | 500 | if (!authInfo.isProxy && authInfo.scheme === 'basic') { |
453 | debug('basic auth handler', authInfo); | 501 | debug('basic auth handler', authInfo); |
454 | basicAuthHandler(mainWindow, authInfo); | 502 | basicAuthHandler(mainWindow!, authInfo); |
455 | } | 503 | } |
456 | }); | 504 | }); |
457 | 505 | ||
458 | // TODO: evaluate if we need to store the authCallback for every service | 506 | // TODO: evaluate if we need to store the authCallback for every service |
459 | ipcMain.on('feature-basic-auth-credentials', (e, { user, password }) => { | 507 | ipcMain.on('feature-basic-auth-credentials', (_e, { user, password }) => { |
460 | debug('Received basic auth credentials', user, '********'); | 508 | debug('Received basic auth credentials', user, '********'); |
461 | 509 | ||
510 | // @ts-expect-error Expected 0 arguments, but got 2. | ||
462 | authCallback(user, password); | 511 | authCallback(user, password); |
463 | authCallback = noop; | 512 | authCallback = noop; |
464 | }); | 513 | }); |
465 | 514 | ||
466 | ipcMain.on('open-browser-window', (e, { url, serviceId }) => { | 515 | ipcMain.on('open-browser-window', (_e, { url, serviceId }) => { |
467 | const serviceSession = session.fromPartition(`persist:service-${serviceId}`); | 516 | const serviceSession = session.fromPartition(`persist:service-${serviceId}`); |
468 | const child = new BrowserWindow({ | 517 | const child = new BrowserWindow({ |
469 | parent: mainWindow, | 518 | parent: mainWindow, |
@@ -481,7 +530,7 @@ ipcMain.on('open-browser-window', (e, { url, serviceId }) => { | |||
481 | 530 | ||
482 | ipcMain.on( | 531 | ipcMain.on( |
483 | 'modifyRequestHeaders', | 532 | 'modifyRequestHeaders', |
484 | (e, { modifiedRequestHeaders, serviceId }) => { | 533 | (_e, { modifiedRequestHeaders, serviceId }) => { |
485 | debug( | 534 | debug( |
486 | `Received modifyRequestHeaders ${modifiedRequestHeaders} for serviceId ${serviceId}`, | 535 | `Received modifyRequestHeaders ${modifiedRequestHeaders} for serviceId ${serviceId}`, |
487 | ); | 536 | ); |
@@ -502,7 +551,7 @@ ipcMain.on( | |||
502 | }, | 551 | }, |
503 | ); | 552 | ); |
504 | 553 | ||
505 | ipcMain.on('knownCertificateHosts', (e, { knownHosts, serviceId }) => { | 554 | ipcMain.on('knownCertificateHosts', (_e, { knownHosts, serviceId }) => { |
506 | debug( | 555 | debug( |
507 | `Received knownCertificateHosts ${knownHosts} for serviceId ${serviceId}`, | 556 | `Received knownCertificateHosts ${knownHosts} for serviceId ${serviceId}`, |
508 | ); | 557 | ); |
@@ -511,7 +560,10 @@ ipcMain.on('knownCertificateHosts', (e, { knownHosts, serviceId }) => { | |||
511 | .setCertificateVerifyProc((request, callback) => { | 560 | .setCertificateVerifyProc((request, callback) => { |
512 | // To know more about these callbacks: https://www.electronjs.org/docs/api/session#sessetcertificateverifyprocproc | 561 | // To know more about these callbacks: https://www.electronjs.org/docs/api/session#sessetcertificateverifyprocproc |
513 | const { hostname } = request; | 562 | const { hostname } = request; |
514 | if (knownHosts.find(item => item.includes(hostname)).length > 0) { | 563 | if ( |
564 | knownHosts.find((item: string | string[]) => item.includes(hostname)) | ||
565 | .length > 0 | ||
566 | ) { | ||
515 | callback(0); | 567 | callback(0); |
516 | } else { | 568 | } else { |
517 | callback(-2); | 569 | callback(-2); |
@@ -522,6 +574,7 @@ ipcMain.on('knownCertificateHosts', (e, { knownHosts, serviceId }) => { | |||
522 | ipcMain.on('feature-basic-auth-cancel', () => { | 574 | ipcMain.on('feature-basic-auth-cancel', () => { |
523 | debug('Cancel basic auth'); | 575 | debug('Cancel basic auth'); |
524 | 576 | ||
577 | // @ts-expect-error Expected 0 arguments, but got 2. | ||
525 | authCallback(null); | 578 | authCallback(null); |
526 | authCallback = noop; | 579 | authCallback = noop; |
527 | }); | 580 | }); |
@@ -530,7 +583,7 @@ ipcMain.on('feature-basic-auth-cancel', () => { | |||
530 | 583 | ||
531 | ipcMain.on('find-in-page', (e, text, options) => { | 584 | ipcMain.on('find-in-page', (e, text, options) => { |
532 | const { sender: webContents } = e; | 585 | const { sender: webContents } = e; |
533 | if (webContents !== mainWindow.webContents && typeof text === 'string') { | 586 | if (webContents !== mainWindow?.webContents && typeof text === 'string') { |
534 | const sanitizedOptions = {}; | 587 | const sanitizedOptions = {}; |
535 | for (const option of ['forward', 'findNext', 'matchCase']) { | 588 | for (const option of ['forward', 'findNext', 'matchCase']) { |
536 | if (option in options) { | 589 | if (option in options) { |
@@ -547,7 +600,7 @@ ipcMain.on('find-in-page', (e, text, options) => { | |||
547 | 600 | ||
548 | ipcMain.on('stop-find-in-page', (e, action) => { | 601 | ipcMain.on('stop-find-in-page', (e, action) => { |
549 | const { sender: webContents } = e; | 602 | const { sender: webContents } = e; |
550 | if (webContents !== mainWindow.webContents) { | 603 | if (webContents !== mainWindow?.webContents) { |
551 | const validActions = [ | 604 | const validActions = [ |
552 | 'clearSelection', | 605 | 'clearSelection', |
553 | 'keepSelection', | 606 | 'keepSelection', |
@@ -560,7 +613,7 @@ ipcMain.on('stop-find-in-page', (e, action) => { | |||
560 | e.returnValue = null; | 613 | e.returnValue = null; |
561 | }); | 614 | }); |
562 | 615 | ||
563 | ipcMain.on('set-spellchecker-locales', (e, { locale, serviceId }) => { | 616 | ipcMain.on('set-spellchecker-locales', (_e, { locale, serviceId }) => { |
564 | if (serviceId === undefined) { | 617 | if (serviceId === undefined) { |
565 | return; | 618 | return; |
566 | } | 619 | } |
@@ -578,7 +631,12 @@ ipcMain.on('set-spellchecker-locales', (e, { locale, serviceId }) => { | |||
578 | app.on('window-all-closed', () => { | 631 | app.on('window-all-closed', () => { |
579 | // On OS X it is common for applications and their menu bar | 632 | // On OS X it is common for applications and their menu bar |
580 | // to stay active until the user quits explicitly with Cmd + Q | 633 | // to stay active until the user quits explicitly with Cmd + Q |
581 | if (retrieveSettingValue('runInBackground')) { | 634 | if ( |
635 | retrieveSettingValue( | ||
636 | 'runInBackground', | ||
637 | DEFAULT_APP_SETTINGS.runInBackground, | ||
638 | ) | ||
639 | ) { | ||
582 | debug('Window: all windows closed, quit app'); | 640 | debug('Window: all windows closed, quit app'); |
583 | app.quit(); | 641 | app.quit(); |
584 | } else { | 642 | } else { |
@@ -589,8 +647,10 @@ app.on('window-all-closed', () => { | |||
589 | app.on('before-quit', event => { | 647 | app.on('before-quit', event => { |
590 | const yesButtonIndex = 0; | 648 | const yesButtonIndex = 0; |
591 | let selection = yesButtonIndex; | 649 | let selection = yesButtonIndex; |
592 | if (retrieveSettingValue('confirmOnQuit')) { | 650 | if ( |
593 | selection = dialog.showMessageBoxSync(app.mainWindow, { | 651 | retrieveSettingValue('confirmOnQuit', DEFAULT_APP_SETTINGS.confirmOnQuit) |
652 | ) { | ||
653 | selection = dialog.showMessageBoxSync(mainWindow!, { | ||
594 | type: 'question', | 654 | type: 'question', |
595 | message: 'Quit', | 655 | message: 'Quit', |
596 | detail: 'Do you really want to quit Ferdi?', | 656 | detail: 'Do you really want to quit Ferdi?', |
@@ -610,12 +670,12 @@ app.on('activate', () => { | |||
610 | if (mainWindow === null) { | 670 | if (mainWindow === null) { |
611 | createWindow(); | 671 | createWindow(); |
612 | } else { | 672 | } else { |
613 | mainWindow.show(); | 673 | mainWindow?.show(); |
614 | } | 674 | } |
615 | }); | 675 | }); |
616 | 676 | ||
617 | app.on('web-contents-created', (createdEvent, contents) => { | 677 | app.on('web-contents-created', (_createdEvent, contents) => { |
618 | contents.on('new-window', (event, url, frameNme, disposition) => { | 678 | contents.on('new-window', (event, _url, _frameNme, disposition) => { |
619 | if (disposition === 'foreground-tab') event.preventDefault(); | 679 | if (disposition === 'foreground-tab') event.preventDefault(); |
620 | }); | 680 | }); |
621 | }); | 681 | }); |
@@ -625,7 +685,7 @@ app.on('will-finish-launching', () => { | |||
625 | app.on('open-url', (event, url) => { | 685 | app.on('open-url', (event, url) => { |
626 | event.preventDefault(); | 686 | event.preventDefault(); |
627 | 687 | ||
628 | onDidLoad(window => { | 688 | onDidLoad((window: BrowserWindow) => { |
629 | debug('open-url event', url); | 689 | debug('open-url event', url); |
630 | handleDeepLink(window, url); | 690 | handleDeepLink(window, url); |
631 | }); | 691 | }); |
diff --git a/src/internal-server/app/Controllers/Http/RecipeController.js b/src/internal-server/app/Controllers/Http/RecipeController.js index d44839db1..1b0ac7035 100644 --- a/src/internal-server/app/Controllers/Http/RecipeController.js +++ b/src/internal-server/app/Controllers/Http/RecipeController.js | |||
@@ -56,7 +56,6 @@ class RecipeController { | |||
56 | let remoteResults = []; | 56 | let remoteResults = []; |
57 | // eslint-disable-next-line eqeqeq | 57 | // eslint-disable-next-line eqeqeq |
58 | if (Env.get('CONNECT_WITH_FRANZ') == 'true') { | 58 | if (Env.get('CONNECT_WITH_FRANZ') == 'true') { |
59 | // eslint-disable-line eqeqeq | ||
60 | remoteResults = JSON.parse( | 59 | remoteResults = JSON.parse( |
61 | await ( | 60 | await ( |
62 | await fetch( | 61 | await fetch( |
@@ -112,7 +111,6 @@ class RecipeController { | |||
112 | } | 111 | } |
113 | // eslint-disable-next-line eqeqeq | 112 | // eslint-disable-next-line eqeqeq |
114 | if (Env.get('CONNECT_WITH_FRANZ') == 'true') { | 113 | if (Env.get('CONNECT_WITH_FRANZ') == 'true') { |
115 | // eslint-disable-line eqeqeq | ||
116 | return response.redirect(`${RECIPES_URL}/download/${service}`); | 114 | return response.redirect(`${RECIPES_URL}/download/${service}`); |
117 | } | 115 | } |
118 | return response.status(400).send({ | 116 | return response.status(400).send({ |
diff --git a/src/internal-server/app/Controllers/Http/ServiceController.js b/src/internal-server/app/Controllers/Http/ServiceController.js index 133473b68..dedb5a12b 100644 --- a/src/internal-server/app/Controllers/Http/ServiceController.js +++ b/src/internal-server/app/Controllers/Http/ServiceController.js | |||
@@ -36,7 +36,7 @@ class ServiceController { | |||
36 | } while ( | 36 | } while ( |
37 | (await Service.query().where('serviceId', serviceId).fetch()).rows | 37 | (await Service.query().where('serviceId', serviceId).fetch()).rows |
38 | .length > 0 | 38 | .length > 0 |
39 | ); // eslint-disable-line no-await-in-loop | 39 | ); |
40 | 40 | ||
41 | await Service.create({ | 41 | await Service.create({ |
42 | serviceId, | 42 | serviceId, |
diff --git a/src/internal-server/app/Controllers/Http/UserController.js b/src/internal-server/app/Controllers/Http/UserController.js index 25b5277bd..2ecc8241c 100644 --- a/src/internal-server/app/Controllers/Http/UserController.js +++ b/src/internal-server/app/Controllers/Http/UserController.js | |||
@@ -171,7 +171,6 @@ class UserController { | |||
171 | return response.status(401).send(errorMessage); | 171 | return response.status(401).send(errorMessage); |
172 | } | 172 | } |
173 | 173 | ||
174 | // eslint-disable-next-line prefer-destructuring | ||
175 | token = content.token; | 174 | token = content.token; |
176 | } catch (error) { | 175 | } catch (error) { |
177 | return response.status(401).send({ | 176 | return response.status(401).send({ |
@@ -300,7 +299,7 @@ class UserController { | |||
300 | } while ( | 299 | } while ( |
301 | (await Workspace.query().where('workspaceId', newWorkspaceId).fetch()) | 300 | (await Workspace.query().where('workspaceId', newWorkspaceId).fetch()) |
302 | .rows.length > 0 | 301 | .rows.length > 0 |
303 | ); // eslint-disable-line no-await-in-loop | 302 | ); |
304 | 303 | ||
305 | if ( | 304 | if ( |
306 | workspace.services && | 305 | workspace.services && |
@@ -340,7 +339,7 @@ class UserController { | |||
340 | } while ( | 339 | } while ( |
341 | (await Service.query().where('serviceId', newServiceId).fetch()).rows | 340 | (await Service.query().where('serviceId', newServiceId).fetch()).rows |
342 | .length > 0 | 341 | .length > 0 |
343 | ); // eslint-disable-line no-await-in-loop | 342 | ); |
344 | 343 | ||
345 | // store the old serviceId as the key for future lookup | 344 | // store the old serviceId as the key for future lookup |
346 | serviceIdTranslation[service.serviceId] = newServiceId; | 345 | serviceIdTranslation[service.serviceId] = newServiceId; |
diff --git a/src/internal-server/app/Controllers/Http/WorkspaceController.js b/src/internal-server/app/Controllers/Http/WorkspaceController.js index 9d461135e..528721f13 100644 --- a/src/internal-server/app/Controllers/Http/WorkspaceController.js +++ b/src/internal-server/app/Controllers/Http/WorkspaceController.js | |||
@@ -27,7 +27,7 @@ class WorkspaceController { | |||
27 | } while ( | 27 | } while ( |
28 | (await Workspace.query().where('workspaceId', workspaceId).fetch()).rows | 28 | (await Workspace.query().where('workspaceId', workspaceId).fetch()).rows |
29 | .length > 0 | 29 | .length > 0 |
30 | ); // eslint-disable-line no-await-in-loop | 30 | ); |
31 | 31 | ||
32 | const order = (await Workspace.all()).rows.length; | 32 | const order = (await Workspace.all()).rows.length; |
33 | const { name } = data; | 33 | const { name } = data; |
diff --git a/src/internal-server/config/app.js b/src/internal-server/config/app.js index 0a1644932..379190734 100644 --- a/src/internal-server/config/app.js +++ b/src/internal-server/config/app.js | |||
@@ -2,7 +2,6 @@ | |||
2 | const Env = use('Env'); | 2 | const Env = use('Env'); |
3 | 3 | ||
4 | module.exports = { | 4 | module.exports = { |
5 | |||
6 | /* | 5 | /* |
7 | |-------------------------------------------------------------------------- | 6 | |-------------------------------------------------------------------------- |
8 | | Application Name | 7 | | Application Name |
diff --git a/src/internal-server/config/bodyParser.js b/src/internal-server/config/bodyParser.js index 8a5406f9e..ef2eedf40 100644 --- a/src/internal-server/config/bodyParser.js +++ b/src/internal-server/config/bodyParser.js | |||
@@ -58,9 +58,7 @@ module.exports = { | |||
58 | | | 58 | | |
59 | */ | 59 | */ |
60 | raw: { | 60 | raw: { |
61 | types: [ | 61 | types: ['text/*'], |
62 | 'text/*', | ||
63 | ], | ||
64 | }, | 62 | }, |
65 | 63 | ||
66 | /* | 64 | /* |
@@ -72,9 +70,7 @@ module.exports = { | |||
72 | | | 70 | | |
73 | */ | 71 | */ |
74 | form: { | 72 | form: { |
75 | types: [ | 73 | types: ['application/x-www-form-urlencoded'], |
76 | 'application/x-www-form-urlencoded', | ||
77 | ], | ||
78 | }, | 74 | }, |
79 | 75 | ||
80 | /* | 76 | /* |
@@ -86,9 +82,7 @@ module.exports = { | |||
86 | | | 82 | | |
87 | */ | 83 | */ |
88 | files: { | 84 | files: { |
89 | types: [ | 85 | types: ['multipart/form-data'], |
90 | 'multipart/form-data', | ||
91 | ], | ||
92 | 86 | ||
93 | /* | 87 | /* |
94 | |-------------------------------------------------------------------------- | 88 | |-------------------------------------------------------------------------- |
diff --git a/src/internal-server/database/migrations/1503250034279_user.js b/src/internal-server/database/migrations/1503250034279_user.js index 80b49020a..d502e4fa0 100644 --- a/src/internal-server/database/migrations/1503250034279_user.js +++ b/src/internal-server/database/migrations/1503250034279_user.js | |||
@@ -3,7 +3,7 @@ const Schema = use('Schema'); | |||
3 | 3 | ||
4 | class UserSchema extends Schema { | 4 | class UserSchema extends Schema { |
5 | up() { | 5 | up() { |
6 | this.create('users', (table) => { | 6 | this.create('users', table => { |
7 | table.increments(); | 7 | table.increments(); |
8 | table.json('settings'); | 8 | table.json('settings'); |
9 | table.timestamps(); | 9 | table.timestamps(); |
diff --git a/src/internal-server/database/migrations/1566385379883_service_schema.js b/src/internal-server/database/migrations/1566385379883_service_schema.js index d887ef193..d8087248f 100644 --- a/src/internal-server/database/migrations/1566385379883_service_schema.js +++ b/src/internal-server/database/migrations/1566385379883_service_schema.js | |||
@@ -3,7 +3,7 @@ const Schema = use('Schema'); | |||
3 | 3 | ||
4 | class ServiceSchema extends Schema { | 4 | class ServiceSchema extends Schema { |
5 | up() { | 5 | up() { |
6 | this.create('services', (table) => { | 6 | this.create('services', table => { |
7 | table.increments(); | 7 | table.increments(); |
8 | table.string('serviceId', 80).notNullable(); | 8 | table.string('serviceId', 80).notNullable(); |
9 | table.string('name', 80).notNullable(); | 9 | table.string('name', 80).notNullable(); |
diff --git a/src/internal-server/database/migrations/1566554231482_recipe_schema.js b/src/internal-server/database/migrations/1566554231482_recipe_schema.js index 514d57600..41bebdacb 100644 --- a/src/internal-server/database/migrations/1566554231482_recipe_schema.js +++ b/src/internal-server/database/migrations/1566554231482_recipe_schema.js | |||
@@ -3,7 +3,7 @@ const Schema = use('Schema'); | |||
3 | 3 | ||
4 | class RecipeSchema extends Schema { | 4 | class RecipeSchema extends Schema { |
5 | up() { | 5 | up() { |
6 | this.create('recipes', (table) => { | 6 | this.create('recipes', table => { |
7 | table.increments(); | 7 | table.increments(); |
8 | table.string('name', 80).notNullable(); | 8 | table.string('name', 80).notNullable(); |
9 | table.string('recipeId', 254).notNullable().unique(); | 9 | table.string('recipeId', 254).notNullable().unique(); |
diff --git a/src/internal-server/database/migrations/1566554359294_workspace_schema.js b/src/internal-server/database/migrations/1566554359294_workspace_schema.js index 421a406b5..3e5385f45 100644 --- a/src/internal-server/database/migrations/1566554359294_workspace_schema.js +++ b/src/internal-server/database/migrations/1566554359294_workspace_schema.js | |||
@@ -3,7 +3,7 @@ const Schema = use('Schema'); | |||
3 | 3 | ||
4 | class WorkspaceSchema extends Schema { | 4 | class WorkspaceSchema extends Schema { |
5 | up() { | 5 | up() { |
6 | this.create('workspaces', (table) => { | 6 | this.create('workspaces', table => { |
7 | table.increments(); | 7 | table.increments(); |
8 | table.string('workspaceId', 80).notNullable().unique(); | 8 | table.string('workspaceId', 80).notNullable().unique(); |
9 | table.string('name', 80).notNullable(); | 9 | table.string('name', 80).notNullable(); |
diff --git a/src/internal-server/start.ts b/src/internal-server/start.ts index 392f7cf41..62311b21e 100644 --- a/src/internal-server/start.ts +++ b/src/internal-server/start.ts | |||
@@ -17,27 +17,27 @@ | |||
17 | 17 | ||
18 | import fold from '@adonisjs/fold'; | 18 | import fold from '@adonisjs/fold'; |
19 | import { Ignitor } from '@adonisjs/ignitor'; | 19 | import { Ignitor } from '@adonisjs/ignitor'; |
20 | import fs from 'fs-extra'; | 20 | import { existsSync, readFile, statSync, chmodSync, writeFile } from 'fs-extra'; |
21 | import path from 'path'; | 21 | import { join } from 'path'; |
22 | import { LOCAL_HOSTNAME } from '../config'; | 22 | import { LOCAL_HOSTNAME } from '../config'; |
23 | import { isWindows } from '../environment'; | 23 | import { isWindows } from '../environment'; |
24 | 24 | ||
25 | process.env.ENV_PATH = path.join(__dirname, 'env.ini'); | 25 | process.env.ENV_PATH = join(__dirname, 'env.ini'); |
26 | 26 | ||
27 | export const server = async (userPath: string, port: number) => { | 27 | export const server = async (userPath: string, port: number) => { |
28 | const dbPath = path.join(userPath, 'server.sqlite'); | 28 | const dbPath = join(userPath, 'server.sqlite'); |
29 | const dbTemplatePath = path.join(__dirname, 'database', 'template.sqlite'); | 29 | const dbTemplatePath = join(__dirname, 'database', 'template.sqlite'); |
30 | 30 | ||
31 | if (!fs.existsSync(dbPath)) { | 31 | if (!existsSync(dbPath)) { |
32 | // Manually copy file | 32 | // Manually copy file |
33 | // We can't use copyFile here as it will cause the file to be readonly on Windows | 33 | // We can't use copyFile here as it will cause the file to be readonly on Windows |
34 | const dbTemplate = await fs.readFile(dbTemplatePath); | 34 | const dbTemplate = await readFile(dbTemplatePath); |
35 | await fs.writeFile(dbPath, dbTemplate); | 35 | await writeFile(dbPath, dbTemplate); |
36 | 36 | ||
37 | // Change permissions to ensure to file is not read-only | 37 | // Change permissions to ensure to file is not read-only |
38 | if (isWindows) { | 38 | if (isWindows) { |
39 | // eslint-disable-next-line no-bitwise | 39 | // eslint-disable-next-line no-bitwise |
40 | fs.chmodSync(dbPath, fs.statSync(dbPath).mode | 146); | 40 | chmodSync(dbPath, statSync(dbPath).mode | 146); |
41 | } | 41 | } |
42 | } | 42 | } |
43 | 43 | ||
diff --git a/src/internal-server/start/app.js b/src/internal-server/start/app.js index 8b1a49f57..7ca544085 100644 --- a/src/internal-server/start/app.js +++ b/src/internal-server/start/app.js | |||
@@ -28,9 +28,7 @@ const providers = [ | |||
28 | | Providers for migrations, tests etc. | 28 | | Providers for migrations, tests etc. |
29 | | | 29 | | |
30 | */ | 30 | */ |
31 | const aceProviders = [ | 31 | const aceProviders = ['@adonisjs/lucid/providers/MigrationsProvider']; |
32 | '@adonisjs/lucid/providers/MigrationsProvider', | ||
33 | ]; | ||
34 | 32 | ||
35 | /* | 33 | /* |
36 | |-------------------------------------------------------------------------- | 34 | |-------------------------------------------------------------------------- |
@@ -57,5 +55,8 @@ const aliases = {}; | |||
57 | const commands = []; | 55 | const commands = []; |
58 | 56 | ||
59 | module.exports = { | 57 | module.exports = { |
60 | providers, aceProviders, aliases, commands, | 58 | providers, |
59 | aceProviders, | ||
60 | aliases, | ||
61 | commands, | ||
61 | }; | 62 | }; |
diff --git a/src/internal-server/start/routes.js b/src/internal-server/start/routes.js index db74479c9..50b9448cf 100644 --- a/src/internal-server/start/routes.js +++ b/src/internal-server/start/routes.js | |||
@@ -66,7 +66,6 @@ Route.group(() => { | |||
66 | // Static responses | 66 | // Static responses |
67 | Route.get('features/:mode?', 'StaticController.features'); | 67 | Route.get('features/:mode?', 'StaticController.features'); |
68 | Route.get('services', 'StaticController.emptyArray'); | 68 | Route.get('services', 'StaticController.emptyArray'); |
69 | Route.get('news', 'StaticController.emptyArray'); | ||
70 | }) | 69 | }) |
71 | .prefix(API_VERSION) | 70 | .prefix(API_VERSION) |
72 | .middleware(OnlyAllowFerdi); | 71 | .middleware(OnlyAllowFerdi); |
diff --git a/src/internal-server/test.ts b/src/internal-server/test.ts index 437a2813b..46d1b1e4a 100644 --- a/src/internal-server/test.ts +++ b/src/internal-server/test.ts | |||
@@ -1,9 +1,9 @@ | |||
1 | import path from 'path'; | 1 | import { join } from 'path'; |
2 | import fs from 'fs-extra'; | 2 | import { ensureDirSync } from 'fs-extra'; |
3 | import { server } from './start'; | 3 | import { server } from './start'; |
4 | 4 | ||
5 | const dummyUserFolder = path.join(__dirname, 'user_data'); | 5 | const dummyUserFolder = join(__dirname, 'user_data'); |
6 | 6 | ||
7 | fs.ensureDirSync(dummyUserFolder); | 7 | ensureDirSync(dummyUserFolder); |
8 | 8 | ||
9 | server(dummyUserFolder, 45_568); | 9 | server(dummyUserFolder, 45_568); |
diff --git a/src/jsUtils.ts b/src/jsUtils.ts index b1baad7c5..d7ea4eb40 100644 --- a/src/jsUtils.ts +++ b/src/jsUtils.ts | |||
@@ -1,3 +1,14 @@ | |||
1 | export const ifUndefinedString = (source: string | undefined | null, defaultValue: string): string => (source !== undefined && source !== null ? source : defaultValue); | 1 | export const ifUndefinedString = ( |
2 | export const ifUndefinedBoolean = (source: boolean | undefined | null, defaultValue: boolean): boolean => Boolean(source !== undefined && source !== null ? source : defaultValue); | 2 | source: string | undefined | null, |
3 | export const ifUndefinedNumber = (source: number | undefined | null, defaultValue: number): number => Number(source !== undefined && source !== null ? source : defaultValue); | 3 | defaultValue: string, |
4 | ): string => (source !== undefined && source !== null ? source : defaultValue); | ||
5 | export const ifUndefinedBoolean = ( | ||
6 | source: boolean | undefined | null, | ||
7 | defaultValue: boolean, | ||
8 | ): boolean => | ||
9 | Boolean(source !== undefined && source !== null ? source : defaultValue); | ||
10 | export const ifUndefinedNumber = ( | ||
11 | source: number | undefined | null, | ||
12 | defaultValue: number, | ||
13 | ): number => | ||
14 | Number(source !== undefined && source !== null ? source : defaultValue); | ||
diff --git a/src/lib/Menu.js b/src/lib/Menu.js index cd86f1669..b1cbaf992 100644 --- a/src/lib/Menu.js +++ b/src/lib/Menu.js | |||
@@ -20,7 +20,7 @@ import { | |||
20 | addNewServiceShortcutKey, | 20 | addNewServiceShortcutKey, |
21 | muteFerdiShortcutKey, | 21 | muteFerdiShortcutKey, |
22 | } from '../environment'; | 22 | } from '../environment'; |
23 | import { aboutAppDetails } from '../environment-remote'; | 23 | import { aboutAppDetails, ferdiVersion } from '../environment-remote'; |
24 | import { todosStore } from '../features/todos'; | 24 | import { todosStore } from '../features/todos'; |
25 | import { todoActions } from '../features/todos/actions'; | 25 | import { todoActions } from '../features/todos/actions'; |
26 | import { workspaceActions } from '../features/workspaces/actions'; | 26 | import { workspaceActions } from '../features/workspaces/actions'; |
@@ -515,7 +515,7 @@ const _titleBarTemplateFactory = (intl, locked) => [ | |||
515 | label: intl.formatMessage(menuItems.changelog), | 515 | label: intl.formatMessage(menuItems.changelog), |
516 | click() { | 516 | click() { |
517 | openExternalUrl( | 517 | openExternalUrl( |
518 | `${GITHUB_FERDI_URL}/ferdi/blob/develop/CHANGELOG.md`, | 518 | `${GITHUB_FERDI_URL}/ferdi/releases/tag/v${ferdiVersion}`, |
519 | true, | 519 | true, |
520 | ); | 520 | ); |
521 | }, | 521 | }, |
diff --git a/src/lib/Tray.js b/src/lib/Tray.js index 7360611cd..e7afc3552 100644 --- a/src/lib/Tray.js +++ b/src/lib/Tray.js | |||
@@ -1,5 +1,11 @@ | |||
1 | import { | 1 | import { |
2 | app, Menu, nativeImage, nativeTheme, systemPreferences, Tray, ipcMain, | 2 | app, |
3 | Menu, | ||
4 | nativeImage, | ||
5 | nativeTheme, | ||
6 | systemPreferences, | ||
7 | Tray, | ||
8 | ipcMain, | ||
3 | } from 'electron'; | 9 | } from 'electron'; |
4 | import { join } from 'path'; | 10 | import { join } from 'path'; |
5 | import macosVersion from 'macos-version'; | 11 | import macosVersion from 'macos-version'; |
@@ -65,7 +71,9 @@ export default class TrayIcon { | |||
65 | 71 | ||
66 | if (appSettings.type === 'app') { | 72 | if (appSettings.type === 'app') { |
67 | const { isAppMuted } = appSettings.data; | 73 | const { isAppMuted } = appSettings.data; |
68 | this.trayMenuTemplate[1].label = isAppMuted ? 'Enable Notifications && Audio' : 'Disable Notifications && Audio'; | 74 | this.trayMenuTemplate[1].label = isAppMuted |
75 | ? 'Enable Notifications && Audio' | ||
76 | : 'Disable Notifications && Audio'; | ||
69 | this.trayMenu = Menu.buildFromTemplate(this.trayMenuTemplate); | 77 | this.trayMenu = Menu.buildFromTemplate(this.trayMenuTemplate); |
70 | if (isLinux) { | 78 | if (isLinux) { |
71 | this.trayIcon.setContextMenu(this.trayMenu); | 79 | this.trayIcon.setContextMenu(this.trayMenu); |
@@ -108,9 +116,12 @@ export default class TrayIcon { | |||
108 | } | 116 | } |
109 | 117 | ||
110 | if (isMac) { | 118 | if (isMac) { |
111 | this.themeChangeSubscriberId = systemPreferences.subscribeNotification('AppleInterfaceThemeChangedNotification', () => { | 119 | this.themeChangeSubscriberId = systemPreferences.subscribeNotification( |
112 | this._refreshIcon(); | 120 | 'AppleInterfaceThemeChangedNotification', |
113 | }); | 121 | () => { |
122 | this._refreshIcon(); | ||
123 | }, | ||
124 | ); | ||
114 | } | 125 | } |
115 | } | 126 | } |
116 | 127 | ||
@@ -150,7 +161,8 @@ export default class TrayIcon { | |||
150 | _getAssetFromIndicator(indicator) { | 161 | _getAssetFromIndicator(indicator) { |
151 | if (indicator === '•') { | 162 | if (indicator === '•') { |
152 | return INDICATOR_TRAY_INDIRECT; | 163 | return INDICATOR_TRAY_INDIRECT; |
153 | } if (indicator !== 0) { | 164 | } |
165 | if (indicator !== 0) { | ||
154 | return INDICATOR_TRAY_UNREAD; | 166 | return INDICATOR_TRAY_UNREAD; |
155 | } | 167 | } |
156 | return INDICATOR_TRAY_PLAIN; | 168 | return INDICATOR_TRAY_PLAIN; |
@@ -159,11 +171,16 @@ export default class TrayIcon { | |||
159 | _refreshIcon() { | 171 | _refreshIcon() { |
160 | if (!this.trayIcon) return; | 172 | if (!this.trayIcon) return; |
161 | 173 | ||
162 | this.trayIcon.setImage(this._getAsset('tray', this._getAssetFromIndicator(this.indicator))); | 174 | this.trayIcon.setImage( |
175 | this._getAsset('tray', this._getAssetFromIndicator(this.indicator)), | ||
176 | ); | ||
163 | 177 | ||
164 | if (isMac) { | 178 | if (isMac) { |
165 | this.trayIcon.setPressedImage( | 179 | this.trayIcon.setPressedImage( |
166 | this._getAsset('tray', `${this._getAssetFromIndicator(this.indicator)}-active`), | 180 | this._getAsset( |
181 | 'tray', | ||
182 | `${this._getAssetFromIndicator(this.indicator)}-active`, | ||
183 | ), | ||
167 | ); | 184 | ); |
168 | } | 185 | } |
169 | } | 186 | } |
@@ -171,12 +188,24 @@ export default class TrayIcon { | |||
171 | _getAsset(type, asset) { | 188 | _getAsset(type, asset) { |
172 | let { platform } = process; | 189 | let { platform } = process; |
173 | 190 | ||
174 | if (isMac && (nativeTheme.shouldUseDarkColors || macosVersion.isGreaterThanOrEqualTo('11'))) { | 191 | if ( |
192 | isMac && | ||
193 | (nativeTheme.shouldUseDarkColors || | ||
194 | macosVersion.isGreaterThanOrEqualTo('11')) | ||
195 | ) { | ||
175 | platform = `${platform}-dark`; | 196 | platform = `${platform}-dark`; |
176 | } | 197 | } |
177 | 198 | ||
178 | return nativeImage.createFromPath(join( | 199 | return nativeImage.createFromPath( |
179 | __dirname, '..', 'assets', 'images', type, platform, `${asset}.${FILE_EXTENSION}`, | 200 | join( |
180 | )); | 201 | __dirname, |
202 | '..', | ||
203 | 'assets', | ||
204 | 'images', | ||
205 | type, | ||
206 | platform, | ||
207 | `${asset}.${FILE_EXTENSION}`, | ||
208 | ), | ||
209 | ); | ||
181 | } | 210 | } |
182 | } | 211 | } |
diff --git a/src/models/News.ts b/src/models/News.ts deleted file mode 100644 index 4fc21f590..000000000 --- a/src/models/News.ts +++ /dev/null | |||
@@ -1,33 +0,0 @@ | |||
1 | import { ifUndefinedString, ifUndefinedBoolean } from '../jsUtils'; | ||
2 | |||
3 | interface INews { | ||
4 | id: string; | ||
5 | message: string; | ||
6 | type: string; | ||
7 | sticky: boolean | undefined; | ||
8 | } | ||
9 | |||
10 | export default class News { | ||
11 | id: string = ''; | ||
12 | |||
13 | message: string = ''; | ||
14 | |||
15 | type: string = 'primary'; | ||
16 | |||
17 | sticky: boolean = false; | ||
18 | |||
19 | constructor(data: INews) { | ||
20 | if (!data) { | ||
21 | throw new Error('News config not valid'); | ||
22 | } | ||
23 | |||
24 | if (!data.id) { | ||
25 | throw new Error('News requires Id'); | ||
26 | } | ||
27 | |||
28 | this.id = data.id; | ||
29 | this.message = ifUndefinedString(data.message, this.message); | ||
30 | this.type = ifUndefinedString(data.type, this.type); | ||
31 | this.sticky = ifUndefinedBoolean(data.sticky, this.sticky); | ||
32 | } | ||
33 | } | ||
diff --git a/src/models/Recipe.ts b/src/models/Recipe.ts index 859c75df0..959b43fd9 100644 --- a/src/models/Recipe.ts +++ b/src/models/Recipe.ts | |||
@@ -1,10 +1,8 @@ | |||
1 | import semver from 'semver'; | 1 | import semver from 'semver'; |
2 | import { pathExistsSync } from 'fs-extra'; | 2 | import { pathExistsSync } from 'fs-extra'; |
3 | import { join } from 'path'; | 3 | import { join } from 'path'; |
4 | import { | 4 | import { DEFAULT_SERVICE_SETTINGS } from '../config'; |
5 | ifUndefinedString, | 5 | import { ifUndefinedString, ifUndefinedBoolean } from '../jsUtils'; |
6 | ifUndefinedBoolean, | ||
7 | } from '../jsUtils'; | ||
8 | 6 | ||
9 | interface IRecipe { | 7 | interface IRecipe { |
10 | id: string; | 8 | id: string; |
@@ -30,20 +28,6 @@ interface IRecipe { | |||
30 | }; | 28 | }; |
31 | } | 29 | } |
32 | 30 | ||
33 | // Note: Do NOT change these default values. If they change, then the corresponding changes in the recipes needs to be done | ||
34 | // TODO: Need to reconcile other properties | ||
35 | const DEFAULT_RECIPE_SETTINGS = { | ||
36 | hasDirectMessages: true, | ||
37 | hasIndirectMessages: false, | ||
38 | hasNotificationSound: false, | ||
39 | hasTeamId: false, | ||
40 | hasCustomUrl: false, | ||
41 | hasHostedOption: false, | ||
42 | allowFavoritesDelineationInUnreadCount: false, | ||
43 | disablewebsecurity: false, | ||
44 | autoHibernate: false, | ||
45 | }; | ||
46 | |||
47 | export default class Recipe { | 31 | export default class Recipe { |
48 | id: string = ''; | 32 | id: string = ''; |
49 | 33 | ||
@@ -57,17 +41,17 @@ export default class Recipe { | |||
57 | 41 | ||
58 | serviceURL: string = ''; | 42 | serviceURL: string = ''; |
59 | 43 | ||
60 | hasDirectMessages: boolean = DEFAULT_RECIPE_SETTINGS.hasDirectMessages; | 44 | hasDirectMessages: boolean = DEFAULT_SERVICE_SETTINGS.hasDirectMessages; |
61 | 45 | ||
62 | hasIndirectMessages: boolean = DEFAULT_RECIPE_SETTINGS.hasIndirectMessages; | 46 | hasIndirectMessages: boolean = DEFAULT_SERVICE_SETTINGS.hasIndirectMessages; |
63 | 47 | ||
64 | hasNotificationSound: boolean = DEFAULT_RECIPE_SETTINGS.hasNotificationSound; | 48 | hasNotificationSound: boolean = DEFAULT_SERVICE_SETTINGS.hasNotificationSound; |
65 | 49 | ||
66 | hasTeamId: boolean = DEFAULT_RECIPE_SETTINGS.hasTeamId; | 50 | hasTeamId: boolean = DEFAULT_SERVICE_SETTINGS.hasTeamId; |
67 | 51 | ||
68 | hasCustomUrl: boolean = DEFAULT_RECIPE_SETTINGS.hasCustomUrl; | 52 | hasCustomUrl: boolean = DEFAULT_SERVICE_SETTINGS.hasCustomUrl; |
69 | 53 | ||
70 | hasHostedOption: boolean = DEFAULT_RECIPE_SETTINGS.hasHostedOption; | 54 | hasHostedOption: boolean = DEFAULT_SERVICE_SETTINGS.hasHostedOption; |
71 | 55 | ||
72 | urlInputPrefix: string = ''; | 56 | urlInputPrefix: string = ''; |
73 | 57 | ||
@@ -75,12 +59,13 @@ export default class Recipe { | |||
75 | 59 | ||
76 | message: string = ''; | 60 | message: string = ''; |
77 | 61 | ||
78 | allowFavoritesDelineationInUnreadCount: boolean = DEFAULT_RECIPE_SETTINGS.allowFavoritesDelineationInUnreadCount; | 62 | allowFavoritesDelineationInUnreadCount: boolean = |
63 | DEFAULT_SERVICE_SETTINGS.allowFavoritesDelineationInUnreadCount; | ||
79 | 64 | ||
80 | disablewebsecurity: boolean = DEFAULT_RECIPE_SETTINGS.disablewebsecurity; | 65 | disablewebsecurity: boolean = DEFAULT_SERVICE_SETTINGS.disablewebsecurity; |
81 | 66 | ||
82 | // TODO: Is this even used? | 67 | // TODO: Is this even used? |
83 | autoHibernate: boolean = DEFAULT_RECIPE_SETTINGS.autoHibernate; | 68 | autoHibernate: boolean = DEFAULT_SERVICE_SETTINGS.autoHibernate; |
84 | 69 | ||
85 | path: string = ''; | 70 | path: string = ''; |
86 | 71 | ||
@@ -98,7 +83,9 @@ export default class Recipe { | |||
98 | } | 83 | } |
99 | 84 | ||
100 | if (!semver.valid(data.version)) { | 85 | if (!semver.valid(data.version)) { |
101 | throw new Error(`Version ${data.version} of recipe '${data.name}' is not a valid semver version`); | 86 | throw new Error( |
87 | `Version ${data.version} of recipe '${data.name}' is not a valid semver version`, | ||
88 | ); | ||
102 | } | 89 | } |
103 | 90 | ||
104 | // from the recipe | 91 | // from the recipe |
@@ -106,19 +93,52 @@ export default class Recipe { | |||
106 | this.name = ifUndefinedString(data.name, this.name); | 93 | this.name = ifUndefinedString(data.name, this.name); |
107 | this.version = ifUndefinedString(data.version, this.version); | 94 | this.version = ifUndefinedString(data.version, this.version); |
108 | this.aliases = data.aliases || this.aliases; | 95 | this.aliases = data.aliases || this.aliases; |
109 | this.serviceURL = ifUndefinedString(data.config.serviceURL, this.serviceURL); | 96 | this.serviceURL = ifUndefinedString( |
110 | this.hasDirectMessages = ifUndefinedBoolean(data.config.hasDirectMessages, this.hasDirectMessages); | 97 | data.config.serviceURL, |
111 | this.hasIndirectMessages = ifUndefinedBoolean(data.config.hasIndirectMessages, this.hasIndirectMessages); | 98 | this.serviceURL, |
112 | this.hasNotificationSound = ifUndefinedBoolean(data.config.hasNotificationSound, this.hasNotificationSound); | 99 | ); |
100 | this.hasDirectMessages = ifUndefinedBoolean( | ||
101 | data.config.hasDirectMessages, | ||
102 | this.hasDirectMessages, | ||
103 | ); | ||
104 | this.hasIndirectMessages = ifUndefinedBoolean( | ||
105 | data.config.hasIndirectMessages, | ||
106 | this.hasIndirectMessages, | ||
107 | ); | ||
108 | this.hasNotificationSound = ifUndefinedBoolean( | ||
109 | data.config.hasNotificationSound, | ||
110 | this.hasNotificationSound, | ||
111 | ); | ||
113 | this.hasTeamId = ifUndefinedBoolean(data.config.hasTeamId, this.hasTeamId); | 112 | this.hasTeamId = ifUndefinedBoolean(data.config.hasTeamId, this.hasTeamId); |
114 | this.hasCustomUrl = ifUndefinedBoolean(data.config.hasCustomUrl, this.hasCustomUrl); | 113 | this.hasCustomUrl = ifUndefinedBoolean( |
115 | this.hasHostedOption = ifUndefinedBoolean(data.config.hasHostedOption, this.hasHostedOption); | 114 | data.config.hasCustomUrl, |
116 | this.urlInputPrefix = ifUndefinedString(data.config.urlInputPrefix, this.urlInputPrefix); | 115 | this.hasCustomUrl, |
117 | this.urlInputSuffix = ifUndefinedString(data.config.urlInputSuffix, this.urlInputSuffix); | 116 | ); |
118 | this.disablewebsecurity = ifUndefinedBoolean(data.config.disablewebsecurity, this.disablewebsecurity); | 117 | this.hasHostedOption = ifUndefinedBoolean( |
119 | this.autoHibernate = ifUndefinedBoolean(data.config.autoHibernate, this.autoHibernate); | 118 | data.config.hasHostedOption, |
119 | this.hasHostedOption, | ||
120 | ); | ||
121 | this.urlInputPrefix = ifUndefinedString( | ||
122 | data.config.urlInputPrefix, | ||
123 | this.urlInputPrefix, | ||
124 | ); | ||
125 | this.urlInputSuffix = ifUndefinedString( | ||
126 | data.config.urlInputSuffix, | ||
127 | this.urlInputSuffix, | ||
128 | ); | ||
129 | this.disablewebsecurity = ifUndefinedBoolean( | ||
130 | data.config.disablewebsecurity, | ||
131 | this.disablewebsecurity, | ||
132 | ); | ||
133 | this.autoHibernate = ifUndefinedBoolean( | ||
134 | data.config.autoHibernate, | ||
135 | this.autoHibernate, | ||
136 | ); | ||
120 | this.message = ifUndefinedString(data.config.message, this.message); | 137 | this.message = ifUndefinedString(data.config.message, this.message); |
121 | this.allowFavoritesDelineationInUnreadCount = ifUndefinedBoolean(data.config.allowFavoritesDelineationInUnreadCount, this.allowFavoritesDelineationInUnreadCount); | 138 | this.allowFavoritesDelineationInUnreadCount = ifUndefinedBoolean( |
139 | data.config.allowFavoritesDelineationInUnreadCount, | ||
140 | this.allowFavoritesDelineationInUnreadCount, | ||
141 | ); | ||
122 | 142 | ||
123 | // computed | 143 | // computed |
124 | this.path = data.path; | 144 | this.path = data.path; |
diff --git a/src/models/Service.js b/src/models/Service.js index 75dfde027..12109fb0a 100644 --- a/src/models/Service.js +++ b/src/models/Service.js | |||
@@ -81,6 +81,8 @@ export default class Service { | |||
81 | 81 | ||
82 | @observable isHibernationEnabled = false; | 82 | @observable isHibernationEnabled = false; |
83 | 83 | ||
84 | @observable isWakeUpEnabled = true; | ||
85 | |||
84 | @observable isHibernationRequested = false; | 86 | @observable isHibernationRequested = false; |
85 | 87 | ||
86 | @observable onlyShowFavoritesInUnreadCount = false; | 88 | @observable onlyShowFavoritesInUnreadCount = false; |
@@ -163,6 +165,10 @@ export default class Service { | |||
163 | data.isHibernationEnabled, | 165 | data.isHibernationEnabled, |
164 | this.isHibernationEnabled, | 166 | this.isHibernationEnabled, |
165 | ); | 167 | ); |
168 | this.isWakeUpEnabled = ifUndefinedBoolean( | ||
169 | data.isWakeUpEnabled, | ||
170 | this.isWakeUpEnabled, | ||
171 | ); | ||
166 | 172 | ||
167 | // Check if "Hibernate on Startup" is enabled and hibernate all services except active one | 173 | // Check if "Hibernate on Startup" is enabled and hibernate all services except active one |
168 | const { hibernateOnStartup } = window.ferdi.stores.settings.app; | 174 | const { hibernateOnStartup } = window.ferdi.stores.settings.app; |
diff --git a/src/models/User.ts b/src/models/User.ts index a04d46d3c..571f1f847 100644 --- a/src/models/User.ts +++ b/src/models/User.ts | |||
@@ -59,7 +59,8 @@ export default class User { | |||
59 | this.beta = data.beta || this.beta; | 59 | this.beta = data.beta || this.beta; |
60 | this.locale = data.locale || this.locale; | 60 | this.locale = data.locale || this.locale; |
61 | 61 | ||
62 | this.isSubscriptionOwner = data.isSubscriptionOwner || this.isSubscriptionOwner; | 62 | this.isSubscriptionOwner = |
63 | data.isSubscriptionOwner || this.isSubscriptionOwner; | ||
63 | 64 | ||
64 | this.team = data.team || this.team; | 65 | this.team = data.team || this.team; |
65 | } | 66 | } |
diff --git a/src/models/UserAgent.js b/src/models/UserAgent.js index 33bf9d072..02ff97db1 100644 --- a/src/models/UserAgent.js +++ b/src/models/UserAgent.js | |||
@@ -63,8 +63,12 @@ export default class UserAgent { | |||
63 | } | 63 | } |
64 | 64 | ||
65 | @computed get userAgent() { | 65 | @computed get userAgent() { |
66 | return this.serviceUserAgentPref | 66 | return ( |
67 | || (this.chromelessUserAgent ? this.userAgentWithoutChromeVersion : this.defaultUserAgent); | 67 | this.serviceUserAgentPref || |
68 | (this.chromelessUserAgent | ||
69 | ? this.userAgentWithoutChromeVersion | ||
70 | : this.defaultUserAgent) | ||
71 | ); | ||
68 | } | 72 | } |
69 | 73 | ||
70 | @action setWebviewReference(webview) { | 74 | @action setWebviewReference(webview) { |
diff --git a/src/routes.js b/src/routes.js index 502ea86f5..9891e5d43 100644 --- a/src/routes.js +++ b/src/routes.js | |||
@@ -1,4 +1,4 @@ | |||
1 | import React, { Component } from 'react'; | 1 | import { Component } from 'react'; |
2 | import PropTypes from 'prop-types'; | 2 | import PropTypes from 'prop-types'; |
3 | import { inject, observer } from 'mobx-react'; | 3 | import { inject, observer } from 'mobx-react'; |
4 | import { Router, Route, IndexRedirect } from 'react-router'; | 4 | import { Router, Route, IndexRedirect } from 'react-router'; |
diff --git a/src/stores.types.ts b/src/stores.types.ts new file mode 100644 index 000000000..e5978a392 --- /dev/null +++ b/src/stores.types.ts | |||
@@ -0,0 +1,367 @@ | |||
1 | export interface FerdiStores { | ||
2 | app: AppStore; | ||
3 | communityRecipes: CommunityRecipesStore; | ||
4 | features: FeaturesStore; | ||
5 | globalError: GlobalErrorStore; | ||
6 | recipePreviews: RecipePreviewsStore; | ||
7 | recipes: RecipeStore; | ||
8 | requests: RequestsStore; | ||
9 | router: RouterStore; | ||
10 | services: ServicesStore; | ||
11 | settings: SettingsStore; | ||
12 | todos: TodosStore; | ||
13 | ui: UIStore; | ||
14 | user: UserStore; | ||
15 | workspaces: WorkspacesStore; | ||
16 | } | ||
17 | |||
18 | interface Stores { | ||
19 | app: AppStore; | ||
20 | communityRecipes: CommunityRecipesStore; | ||
21 | features: FeaturesStore; | ||
22 | globalError: GlobalErrorStore; | ||
23 | recipePreviews: RecipePreviewsStore; | ||
24 | recipes: RecipeStore; | ||
25 | requests: RequestsStore; | ||
26 | router: RouterStore; | ||
27 | services: ServicesStore; | ||
28 | settings: SettingsStore; | ||
29 | todos: TodosStore; | ||
30 | ui: UIStore; | ||
31 | user: UserStore; | ||
32 | workspaces: WorkspacesStore; | ||
33 | } | ||
34 | |||
35 | interface Actions { | ||
36 | app: AppStore; | ||
37 | recipePreviews: RecipePreviewsStore; | ||
38 | recipes: RecipeStore; | ||
39 | requests: RequestsStore; | ||
40 | services: ServicesStore; | ||
41 | settings: SettingsStore; | ||
42 | todos: TodosStore; | ||
43 | ui: UIStore; | ||
44 | user: UserStore; | ||
45 | workspaces: WorkspacesStore; | ||
46 | } | ||
47 | |||
48 | interface Api { | ||
49 | app: AppStore; | ||
50 | features: FeaturesStore; | ||
51 | local: {}; | ||
52 | recipePreviews: RecipePreviewsStore; | ||
53 | recipes: RecipeStore; | ||
54 | services: ServicesStore; | ||
55 | user: UserStore; | ||
56 | } | ||
57 | |||
58 | interface AppStore { | ||
59 | actions: Actions; | ||
60 | accentColor: string; | ||
61 | api: Api; | ||
62 | authRequestFailed: () => void; | ||
63 | autoLaunchOnStart: () => void; | ||
64 | clearAppCacheRequest: () => void; | ||
65 | dictionaries: []; | ||
66 | fetchDataInterval: 4; | ||
67 | getAppCacheSizeRequest: () => void; | ||
68 | healthCheckRequest: () => void; | ||
69 | isClearingAllCache: () => void; | ||
70 | isFocused: () => void; | ||
71 | isFullScreen: () => void; | ||
72 | isOnline: () => void; | ||
73 | isSystemDarkModeEnabled: () => void; | ||
74 | isSystemMuteOverridden: () => void; | ||
75 | locale: () => void; | ||
76 | stores: Stores; | ||
77 | timeOfflineStart: () => void; | ||
78 | timeSuspensionStart: () => void; | ||
79 | updateStatus: () => void; | ||
80 | updateStatusTypes: { | ||
81 | CHECKING: 'CHECKING'; | ||
82 | AVAILABLE: 'AVAILABLE'; | ||
83 | NOT_AVAILABLE: 'NOT_AVAILABLE'; | ||
84 | DOWNLOADED: 'DOWNLOADED'; | ||
85 | FAILED: 'FAILED'; | ||
86 | }; | ||
87 | _reactions: any[]; | ||
88 | _status: () => void; | ||
89 | actionStatus: () => void; | ||
90 | cacheSize: () => void; | ||
91 | debugInfo: () => void; | ||
92 | } | ||
93 | |||
94 | interface CommunityRecipesStore { | ||
95 | actions: Actions; | ||
96 | stores: Stores; | ||
97 | _actions: []; | ||
98 | _reactions: []; | ||
99 | communityRecipes: () => void; | ||
100 | } | ||
101 | |||
102 | interface FeaturesStore { | ||
103 | actions: Actions; | ||
104 | api: Api; | ||
105 | defaultFeaturesRequest: () => void; | ||
106 | features: () => void; | ||
107 | featuresRequest: () => void; | ||
108 | stores: Stores; | ||
109 | _reactions: any[]; | ||
110 | _status: () => void; | ||
111 | _updateFeatures: () => void; | ||
112 | actionStatus: () => void; | ||
113 | anonymousFeatures: () => void; | ||
114 | } | ||
115 | |||
116 | interface GlobalErrorStore { | ||
117 | actions: Actions; | ||
118 | api: Api; | ||
119 | error: () => void; | ||
120 | messages: () => void; | ||
121 | response: () => void; | ||
122 | stores: Stores; | ||
123 | _handleRequests: () => void; | ||
124 | _reactions: []; | ||
125 | _status: () => void; | ||
126 | actionStatus: () => void; | ||
127 | } | ||
128 | |||
129 | interface RecipePreviewsStore { | ||
130 | actions: Actions; | ||
131 | allRecipePreviewsRequest: () => void; | ||
132 | api: Api; | ||
133 | searchRecipePreviewsRequest: () => void; | ||
134 | stores: Stores; | ||
135 | _reactions: []; | ||
136 | _status: () => void; | ||
137 | actionStatus: () => void; | ||
138 | all: () => void; | ||
139 | dev: () => void; | ||
140 | searchResults: () => void; | ||
141 | } | ||
142 | |||
143 | interface RecipeStore { | ||
144 | actions: Actions; | ||
145 | allRecipesRequest: () => void; | ||
146 | api: Api; | ||
147 | getRecipeUpdatesRequest: () => void; | ||
148 | installRecipeRequest: () => void; | ||
149 | stores: Stores; | ||
150 | _reactions: any[]; | ||
151 | _status: () => void; | ||
152 | actionStatus: () => void; | ||
153 | active: () => void; | ||
154 | all: () => void; | ||
155 | recipeIdForServices: () => void; | ||
156 | } | ||
157 | |||
158 | interface RequestsStore { | ||
159 | actions: Actions; | ||
160 | api: Api; | ||
161 | localServerPort: () => void; | ||
162 | retries: number; | ||
163 | retryDelay: number; | ||
164 | servicesRequest: () => void; | ||
165 | showRequiredRequestsError: () => void; | ||
166 | stores: Stores; | ||
167 | userInfoRequest: () => void; | ||
168 | _reactions: any[]; | ||
169 | _status: () => void; | ||
170 | actionStatus: () => void; | ||
171 | areRequiredRequestsLoading: () => void; | ||
172 | areRequiredRequestsSuccessful: () => void; | ||
173 | } | ||
174 | |||
175 | interface RouterStore { | ||
176 | go: () => void; | ||
177 | goBack: () => void; | ||
178 | goForward: () => void; | ||
179 | history: () => void; | ||
180 | location: () => void; | ||
181 | push: () => void; | ||
182 | replace: () => void; | ||
183 | } | ||
184 | |||
185 | export interface ServicesStore { | ||
186 | actions: Actions; | ||
187 | api: Api; | ||
188 | clearCacheRequest: () => void; | ||
189 | createServiceRequest: () => void; | ||
190 | deleteServiceRequest: () => void; | ||
191 | filterNeedle: () => void; | ||
192 | lastUsedServices: () => void; | ||
193 | reorderServicesRequest: () => void; | ||
194 | serviceMaintenanceTick: () => void; | ||
195 | stores: Stores; | ||
196 | updateServiceRequest: () => void; | ||
197 | _reactions: any[]; | ||
198 | _status: () => void; | ||
199 | actionStatus: () => void; | ||
200 | active: () => void; | ||
201 | activeSettings: () => void; | ||
202 | all: () => void; | ||
203 | allDisplayed: () => void; | ||
204 | allDisplayedUnordered: () => void; | ||
205 | enabled: () => void; | ||
206 | filtered: () => void; | ||
207 | isTodosServiceActive: () => void; | ||
208 | isTodosServiceAdded: () => void; | ||
209 | } | ||
210 | |||
211 | interface SettingsStore { | ||
212 | actions: Actions; | ||
213 | api: Api; | ||
214 | fileSystemSettingsTypes: any[]; | ||
215 | startup: boolean; | ||
216 | stores: Stores; | ||
217 | updateAppSettingsRequest: () => void; | ||
218 | _fileSystemSettingsCache: () => void; | ||
219 | _reactions: []; | ||
220 | _status: () => void; | ||
221 | actionStatus: () => void; | ||
222 | all: () => void; | ||
223 | app: AppStore; | ||
224 | migration: () => void; | ||
225 | proxy: () => void; | ||
226 | service: ServicesStore; | ||
227 | stats: () => void; | ||
228 | } | ||
229 | |||
230 | interface TodosStore { | ||
231 | actions: Actions; | ||
232 | api: Api; | ||
233 | isFeatureActive: () => void; | ||
234 | isFeatureEnabled: () => void; | ||
235 | isInitialized: true; | ||
236 | stores: Stores; | ||
237 | userAgentModel: () => void; | ||
238 | webview: () => void; | ||
239 | _actions: any[]; | ||
240 | _allReactions: any[]; | ||
241 | _firstLaunchReaction: () => void; | ||
242 | _goToService: () => void; | ||
243 | _handleNewWindowEvent: () => void; | ||
244 | _onTodosClientInitialized: () => void; | ||
245 | _openDevTools: () => void; | ||
246 | _reactions: any[]; | ||
247 | _reload: () => void; | ||
248 | _routeCheckReaction: () => void; | ||
249 | _setFeatureEnabledReaction: () => void; | ||
250 | _updateSettings: () => void; | ||
251 | _updateTodosConfig: () => void; | ||
252 | isFeatureEnabledByUser: () => void; | ||
253 | isTodoUrlValid: () => void; | ||
254 | isTodosPanelForceHidden: () => void; | ||
255 | isTodosPanelVisible: () => void; | ||
256 | isUsingPredefinedTodoServer: () => void; | ||
257 | settings: () => void; | ||
258 | todoRecipeId: () => void; | ||
259 | todoUrl: () => void; | ||
260 | userAgent: () => void; | ||
261 | width: () => void; | ||
262 | _handleClientMessage: () => void; | ||
263 | _handleHostMessage: () => void; | ||
264 | _resize: () => void; | ||
265 | _setTodosWebview: () => void; | ||
266 | _toggleTodosFeatureVisibility: () => void; | ||
267 | _toggleTodosPanel: () => void; | ||
268 | } | ||
269 | |||
270 | interface UIStore { | ||
271 | actions: Actions; | ||
272 | api: Api; | ||
273 | isOsDarkThemeActive: () => void; | ||
274 | showServicesUpdatedInfoBar: () => void; | ||
275 | stores: Stores; | ||
276 | _reactions: []; | ||
277 | _status: () => void; | ||
278 | actionStatus: () => void; | ||
279 | isDarkThemeActive: () => void; | ||
280 | isSplitModeActive: () => void; | ||
281 | showMessageBadgesEvenWhenMuted: () => void; | ||
282 | theme: () => void; | ||
283 | } | ||
284 | |||
285 | interface UserStore { | ||
286 | BASE_ROUTE: '/auth'; | ||
287 | CHANGE_SERVER_ROUTE: '/auth/server'; | ||
288 | IMPORT_ROUTE: '/auth/signup/import'; | ||
289 | INVITE_ROUTE: '/auth/signup/invite'; | ||
290 | LOGIN_ROUTE: '/auth/login'; | ||
291 | LOGOUT_ROUTE: '/auth/logout'; | ||
292 | PASSWORD_ROUTE: '/auth/password'; | ||
293 | SETUP_ROUTE: '/auth/signup/setup'; | ||
294 | SIGNUP_ROUTE: '/auth/signup'; | ||
295 | WELCOME_ROUTE: '/auth/welcome'; | ||
296 | accountType: () => void; | ||
297 | actionStatus: () => void; | ||
298 | actions: Actions; | ||
299 | api: Api; | ||
300 | authToken: () => void; | ||
301 | deleteAccountRequest: () => void; | ||
302 | fetchUserInfoInterval: null; | ||
303 | getLegacyServicesRequest: () => void; | ||
304 | getUserInfoRequest: () => void; | ||
305 | hasCompletedSignup: () => void; | ||
306 | id: () => void; | ||
307 | inviteRequest: () => void; | ||
308 | isImportLegacyServicesCompleted: () => void; | ||
309 | isImportLegacyServicesExecuting: () => void; | ||
310 | isLoggingOut: () => void; | ||
311 | loginRequest: () => void; | ||
312 | logoutReason: () => void; | ||
313 | logoutReasonTypes: { SERVER: 'SERVER' }; | ||
314 | passwordRequest: () => void; | ||
315 | signupRequest: () => void; | ||
316 | stores: Stores; | ||
317 | updateUserInfoRequest: () => void; | ||
318 | userData: () => void; | ||
319 | _reactions: any[]; | ||
320 | _requireAuthenticatedUser: () => void; | ||
321 | _status: () => void; | ||
322 | changeServerRoute: () => void; | ||
323 | data: () => void; | ||
324 | importRoute: () => void; | ||
325 | inviteRoute: () => void; | ||
326 | isLoggedIn: () => void; | ||
327 | isTokenExpired: () => void; | ||
328 | legacyServices: () => void; | ||
329 | loginRoute: () => void; | ||
330 | logoutRoute: () => void; | ||
331 | passwordRoute: () => void; | ||
332 | setupRoute: () => void; | ||
333 | signupRoute: () => void; | ||
334 | team: () => void; | ||
335 | } | ||
336 | |||
337 | export interface WorkspacesStore { | ||
338 | activeWorkspace: () => void; | ||
339 | delete: ({ workspace }) => void; | ||
340 | update: ({ workspace }) => void; | ||
341 | create: ({ workspace }) => void; | ||
342 | edit: ({ workspace }) => void; | ||
343 | saving: boolean; | ||
344 | filterServicesByActiveWorkspace: () => void; | ||
345 | isFeatureActive: () => void; | ||
346 | isFeatureEnabled: () => void; | ||
347 | isSettingsRouteActive: () => void; | ||
348 | isSwitchingWorkspace: () => void; | ||
349 | isWorkspaceDrawerOpen: () => void; | ||
350 | nextWorkspace: () => void; | ||
351 | stores: Stores; | ||
352 | workspaceBeingEdited: () => void; | ||
353 | _actions: any[]; | ||
354 | _activateLastUsedWorkspaceReaction: () => void; | ||
355 | _allActions: any[]; | ||
356 | _allReactions: any[]; | ||
357 | _cleanupInvalidServiceReferences: () => void; | ||
358 | _getWorkspaceById: () => void; | ||
359 | _openDrawerWithSettingsReaction: () => void; | ||
360 | _reactions: any[]; | ||
361 | _setActiveServiceOnWorkspaceSwitchReaction: () => void; | ||
362 | _setFeatureEnabledReaction: () => void; | ||
363 | _setWorkspaceBeingEditedReaction: () => void; | ||
364 | _toggleKeepAllWorkspacesLoadedSetting: () => void; | ||
365 | _updateSettings: () => void; | ||
366 | _wasDrawerOpenBeforeSettingsRoute: null; | ||
367 | } | ||
diff --git a/src/stores/AppStore.js b/src/stores/AppStore.js index 3d9d2b551..81cef3775 100644 --- a/src/stores/AppStore.js +++ b/src/stores/AppStore.js | |||
@@ -17,16 +17,8 @@ import { readJsonSync } from 'fs-extra'; | |||
17 | import Store from './lib/Store'; | 17 | import Store from './lib/Store'; |
18 | import Request from './lib/Request'; | 18 | import Request from './lib/Request'; |
19 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; | 19 | import { CHECK_INTERVAL, DEFAULT_APP_SETTINGS } from '../config'; |
20 | import { | 20 | import { isMac, electronVersion, osRelease } from '../environment'; |
21 | isMac, | 21 | import { ferdiVersion, userDataPath, ferdiLocale } from '../environment-remote'; |
22 | electronVersion, | ||
23 | osRelease, | ||
24 | } from '../environment'; | ||
25 | import { | ||
26 | ferdiVersion, | ||
27 | userDataPath, | ||
28 | ferdiLocale, | ||
29 | } from '../environment-remote'; | ||
30 | import locales from '../i18n/translations'; | 22 | import locales from '../i18n/translations'; |
31 | import { getLocale } from '../helpers/i18n-helpers'; | 23 | import { getLocale } from '../helpers/i18n-helpers'; |
32 | 24 | ||
@@ -41,8 +33,6 @@ const debug = require('debug')('Ferdi:AppStore'); | |||
41 | 33 | ||
42 | const mainWindow = getCurrentWindow(); | 34 | const mainWindow = getCurrentWindow(); |
43 | 35 | ||
44 | const defaultLocale = DEFAULT_APP_SETTINGS.locale; | ||
45 | |||
46 | const executablePath = isMac ? remoteProcess.execPath : process.execPath; | 36 | const executablePath = isMac ? remoteProcess.execPath : process.execPath; |
47 | const autoLauncher = new AutoLaunch({ | 37 | const autoLauncher = new AutoLaunch({ |
48 | name: 'Ferdi', | 38 | name: 'Ferdi', |
@@ -80,9 +70,9 @@ export default class AppStore extends Store { | |||
80 | 70 | ||
81 | @observable timeOfflineStart; | 71 | @observable timeOfflineStart; |
82 | 72 | ||
83 | @observable updateStatus = null; | 73 | @observable updateStatus = ''; |
84 | 74 | ||
85 | @observable locale = defaultLocale; | 75 | @observable locale = ferdiLocale; |
86 | 76 | ||
87 | @observable isSystemMuteOverridden = false; | 77 | @observable isSystemMuteOverridden = false; |
88 | 78 | ||
@@ -163,9 +153,6 @@ export default class AppStore extends Store { | |||
163 | this.stores.features.featuresRequest.invalidate({ | 153 | this.stores.features.featuresRequest.invalidate({ |
164 | immediately: true, | 154 | immediately: true, |
165 | }); | 155 | }); |
166 | this.stores.news.latestNewsRequest.invalidate({ | ||
167 | immediately: true, | ||
168 | }); | ||
169 | }, ms('60m')); | 156 | }, ms('60m')); |
170 | 157 | ||
171 | // Check for updates once every 4 hours | 158 | // Check for updates once every 4 hours |
@@ -408,7 +395,7 @@ export default class AppStore extends Store { | |||
408 | } | 395 | } |
409 | 396 | ||
410 | @action _resetUpdateStatus() { | 397 | @action _resetUpdateStatus() { |
411 | this.updateStatus = null; | 398 | this.updateStatus = ''; |
412 | } | 399 | } |
413 | 400 | ||
414 | @action _healthCheck() { | 401 | @action _healthCheck() { |
@@ -480,23 +467,13 @@ export default class AppStore extends Store { | |||
480 | } | 467 | } |
481 | 468 | ||
482 | _setLocale() { | 469 | _setLocale() { |
483 | let locale; | 470 | if (this.stores.user.isLoggedIn && this.stores.user.data.locale) { |
484 | if (this.stores.user.isLoggedIn) { | 471 | this.locale = this.stores.user.data.locale; |
485 | locale = this.stores.user.data.locale; | 472 | } else if (!this.locale) { |
486 | } | ||
487 | |||
488 | if ( | ||
489 | locale && | ||
490 | Object.prototype.hasOwnProperty.call(locales, locale) && | ||
491 | locale !== this.locale | ||
492 | ) { | ||
493 | this.locale = locale; | ||
494 | } else if (!locale) { | ||
495 | this.locale = this._getDefaultLocale(); | 473 | this.locale = this._getDefaultLocale(); |
496 | } | 474 | } |
497 | 475 | ||
498 | moment.locale(this.locale); | 476 | moment.locale(this.locale); |
499 | |||
500 | debug(`Set locale to "${this.locale}"`); | 477 | debug(`Set locale to "${this.locale}"`); |
501 | } | 478 | } |
502 | 479 | ||
@@ -504,7 +481,6 @@ export default class AppStore extends Store { | |||
504 | return getLocale({ | 481 | return getLocale({ |
505 | locale: ferdiLocale, | 482 | locale: ferdiLocale, |
506 | locales, | 483 | locales, |
507 | defaultLocale, | ||
508 | fallbackLocale: DEFAULT_APP_SETTINGS.fallbackLocale, | 484 | fallbackLocale: DEFAULT_APP_SETTINGS.fallbackLocale, |
509 | }); | 485 | }); |
510 | } | 486 | } |
@@ -523,10 +499,12 @@ export default class AppStore extends Store { | |||
523 | _handleFullScreen() { | 499 | _handleFullScreen() { |
524 | const body = document.querySelector('body'); | 500 | const body = document.querySelector('body'); |
525 | 501 | ||
526 | if (this.isFullScreen) { | 502 | if (body) { |
527 | body.classList.add('isFullScreen'); | 503 | if (this.isFullScreen) { |
528 | } else { | 504 | body.classList.add('isFullScreen'); |
529 | body.classList.remove('isFullScreen'); | 505 | } else { |
506 | body.classList.remove('isFullScreen'); | ||
507 | } | ||
530 | } | 508 | } |
531 | } | 509 | } |
532 | 510 | ||
diff --git a/src/stores/NewsStore.js b/src/stores/NewsStore.js deleted file mode 100644 index 66a17cb29..000000000 --- a/src/stores/NewsStore.js +++ /dev/null | |||
@@ -1,55 +0,0 @@ | |||
1 | import { computed, observable } from 'mobx'; | ||
2 | import { remove } from 'lodash'; | ||
3 | |||
4 | import Store from './lib/Store'; | ||
5 | import CachedRequest from './lib/CachedRequest'; | ||
6 | import Request from './lib/Request'; | ||
7 | import { CHECK_INTERVAL } from '../config'; | ||
8 | |||
9 | export default class NewsStore extends Store { | ||
10 | @observable latestNewsRequest = new CachedRequest(this.api.news, 'latest'); | ||
11 | |||
12 | @observable hideNewsRequest = new Request(this.api.news, 'hide'); | ||
13 | |||
14 | constructor(...args) { | ||
15 | super(...args); | ||
16 | |||
17 | // Register action handlers | ||
18 | this.actions.news.hide.listen(this._hide.bind(this)); | ||
19 | this.actions.user.logout.listen(this._resetNewsRequest.bind(this)); | ||
20 | } | ||
21 | |||
22 | setup() { | ||
23 | // Check for news updates every couple of hours | ||
24 | setInterval(() => { | ||
25 | if (this.latestNewsRequest.wasExecuted && this.stores.user.isLoggedIn) { | ||
26 | this.latestNewsRequest.invalidate({ immediately: true }); | ||
27 | } | ||
28 | }, CHECK_INTERVAL); | ||
29 | } | ||
30 | |||
31 | @computed get latest() { | ||
32 | return this.latestNewsRequest.execute().result || []; | ||
33 | } | ||
34 | |||
35 | // Actions | ||
36 | _hide({ newsId }) { | ||
37 | this.hideNewsRequest.execute(newsId); | ||
38 | |||
39 | this.latestNewsRequest.invalidate().patch((result) => { | ||
40 | // TODO: check if we can use mobx.array remove | ||
41 | remove(result, (n) => n.id === newsId); | ||
42 | }); | ||
43 | } | ||
44 | |||
45 | /** | ||
46 | * Reset the news request when current user logs out so that when another user | ||
47 | * logs in again without an app restart, the request will be fetched again and | ||
48 | * the news will be shown to the user. | ||
49 | * | ||
50 | * @private | ||
51 | */ | ||
52 | _resetNewsRequest() { | ||
53 | this.latestNewsRequest.reset(); | ||
54 | } | ||
55 | } | ||
diff --git a/src/stores/RecipePreviewsStore.js b/src/stores/RecipePreviewsStore.js index f4e39306c..e01e8fc6f 100644 --- a/src/stores/RecipePreviewsStore.js +++ b/src/stores/RecipePreviewsStore.js | |||
@@ -5,9 +5,15 @@ import CachedRequest from './lib/CachedRequest'; | |||
5 | import Request from './lib/Request'; | 5 | import Request from './lib/Request'; |
6 | 6 | ||
7 | export default class RecipePreviewsStore extends Store { | 7 | export default class RecipePreviewsStore extends Store { |
8 | @observable allRecipePreviewsRequest = new CachedRequest(this.api.recipePreviews, 'all'); | 8 | @observable allRecipePreviewsRequest = new CachedRequest( |
9 | this.api.recipePreviews, | ||
10 | 'all', | ||
11 | ); | ||
9 | 12 | ||
10 | @observable searchRecipePreviewsRequest = new Request(this.api.recipePreviews, 'search'); | 13 | @observable searchRecipePreviewsRequest = new Request( |
14 | this.api.recipePreviews, | ||
15 | 'search', | ||
16 | ); | ||
11 | 17 | ||
12 | constructor(...args) { | 18 | constructor(...args) { |
13 | super(...args); | 19 | super(...args); |
@@ -25,7 +31,7 @@ export default class RecipePreviewsStore extends Store { | |||
25 | } | 31 | } |
26 | 32 | ||
27 | @computed get dev() { | 33 | @computed get dev() { |
28 | return this.stores.recipes.all.filter((r) => r.local); | 34 | return this.stores.recipes.all.filter(r => r.local); |
29 | } | 35 | } |
30 | 36 | ||
31 | // Actions | 37 | // Actions |
diff --git a/src/stores/ServicesStore.js b/src/stores/ServicesStore.js index ce6675866..4f7ad7442 100644 --- a/src/stores/ServicesStore.js +++ b/src/stores/ServicesStore.js | |||
@@ -15,7 +15,7 @@ import { | |||
15 | getDevRecipeDirectory, | 15 | getDevRecipeDirectory, |
16 | } from '../helpers/recipe-helpers'; | 16 | } from '../helpers/recipe-helpers'; |
17 | import { workspaceStore } from '../features/workspaces'; | 17 | import { workspaceStore } from '../features/workspaces'; |
18 | import { KEEP_WS_LOADED_USID } from '../config'; | 18 | import { DEFAULT_SERVICE_SETTINGS, KEEP_WS_LOADED_USID } from '../config'; |
19 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; | 19 | import { SPELLCHECKER_LOCALES } from '../i18n/languages'; |
20 | import { ferdiVersion } from '../environment-remote'; | 20 | import { ferdiVersion } from '../environment-remote'; |
21 | 21 | ||
@@ -224,6 +224,7 @@ export default class ServicesStore extends Store { | |||
224 | } | 224 | } |
225 | 225 | ||
226 | if ( | 226 | if ( |
227 | service.isWakeUpEnabled && | ||
227 | service.lastHibernated && | 228 | service.lastHibernated && |
228 | Number(this.stores.settings.all.app.wakeUpStrategy) > 0 && | 229 | Number(this.stores.settings.all.app.wakeUpStrategy) > 0 && |
229 | Date.now() - service.lastHibernated > | 230 | Date.now() - service.lastHibernated > |
@@ -394,16 +395,15 @@ export default class ServicesStore extends Store { | |||
394 | } | 395 | } |
395 | 396 | ||
396 | // set default values for serviceData | 397 | // set default values for serviceData |
397 | // eslint-disable-next-line prefer-object-spread | ||
398 | // TODO: How is this different from the defaults of the recipe in 'src/models/Recipe' file? | ||
399 | serviceData = { | 398 | serviceData = { |
400 | isEnabled: true, | 399 | isEnabled: DEFAULT_SERVICE_SETTINGS.isEnabled, |
401 | isHibernationEnabled: false, | 400 | isHibernationEnabled: DEFAULT_SERVICE_SETTINGS.isHibernationEnabled, |
402 | isNotificationEnabled: true, | 401 | isWakeUpEnabled: DEFAULT_SERVICE_SETTINGS.isWakeUpEnabled, |
403 | isBadgeEnabled: true, | 402 | isNotificationEnabled: DEFAULT_SERVICE_SETTINGS.isNotificationEnabled, |
404 | isMuted: false, | 403 | isBadgeEnabled: DEFAULT_SERVICE_SETTINGS.isBadgeEnabled, |
405 | customIcon: false, | 404 | isMuted: DEFAULT_SERVICE_SETTINGS.isMuted, |
406 | isDarkModeEnabled: false, | 405 | customIcon: DEFAULT_SERVICE_SETTINGS.customIcon, |
406 | isDarkModeEnabled: DEFAULT_SERVICE_SETTINGS.isDarkModeEnabled, | ||
407 | spellcheckerLanguage: | 407 | spellcheckerLanguage: |
408 | SPELLCHECKER_LOCALES[this.stores.settings.app.spellcheckerLanguage], | 408 | SPELLCHECKER_LOCALES[this.stores.settings.app.spellcheckerLanguage], |
409 | userAgentPref: '', | 409 | userAgentPref: '', |
@@ -888,6 +888,7 @@ export default class ServicesStore extends Store { | |||
888 | return this.actions.todos.reload(); | 888 | return this.actions.todos.reload(); |
889 | } | 889 | } |
890 | 890 | ||
891 | if (!service.webview) return; | ||
891 | return service.webview.loadURL(service.url); | 892 | return service.webview.loadURL(service.url); |
892 | } | 893 | } |
893 | 894 | ||
@@ -995,7 +996,7 @@ export default class ServicesStore extends Store { | |||
995 | const service = this.one(serviceId); | 996 | const service = this.one(serviceId); |
996 | if (service.isTodosService) { | 997 | if (service.isTodosService) { |
997 | this.actions.todos.openDevTools(); | 998 | this.actions.todos.openDevTools(); |
998 | } else { | 999 | } else if (service.webview) { |
999 | service.webview.openDevTools(); | 1000 | service.webview.openDevTools(); |
1000 | } | 1001 | } |
1001 | } | 1002 | } |
@@ -1147,7 +1148,7 @@ export default class ServicesStore extends Store { | |||
1147 | const { isAttached } = service; | 1148 | const { isAttached } = service; |
1148 | const isMuted = isAppMuted || service.isMuted; | 1149 | const isMuted = isAppMuted || service.isMuted; |
1149 | 1150 | ||
1150 | if (isAttached) { | 1151 | if (isAttached && service.webview) { |
1151 | service.webview.audioMuted = isMuted; | 1152 | service.webview.audioMuted = isMuted; |
1152 | } | 1153 | } |
1153 | } | 1154 | } |
diff --git a/src/stores/SettingsStore.js b/src/stores/SettingsStore.js index ec80fee7c..ac9356404 100644 --- a/src/stores/SettingsStore.js +++ b/src/stores/SettingsStore.js | |||
@@ -2,7 +2,11 @@ import { ipcRenderer } from 'electron'; | |||
2 | import { getCurrentWindow } from '@electron/remote'; | 2 | import { getCurrentWindow } from '@electron/remote'; |
3 | import { action, computed, observable, reaction } from 'mobx'; | 3 | import { action, computed, observable, reaction } from 'mobx'; |
4 | import localStorage from 'mobx-localstorage'; | 4 | import localStorage from 'mobx-localstorage'; |
5 | import { DEFAULT_APP_SETTINGS, FILE_SYSTEM_SETTINGS_TYPES, LOCAL_SERVER } from '../config'; | 5 | import { |
6 | DEFAULT_APP_SETTINGS, | ||
7 | FILE_SYSTEM_SETTINGS_TYPES, | ||
8 | LOCAL_SERVER, | ||
9 | } from '../config'; | ||
6 | import { hash } from '../helpers/password-helpers'; | 10 | import { hash } from '../helpers/password-helpers'; |
7 | import Request from './lib/Request'; | 11 | import Request from './lib/Request'; |
8 | import Store from './lib/Store'; | 12 | import Store from './lib/Store'; |
diff --git a/src/stores/UIStore.ts b/src/stores/UIStore.ts index 6ab63c2ee..6a9597006 100644 --- a/src/stores/UIStore.ts +++ b/src/stores/UIStore.ts | |||
@@ -1,7 +1,7 @@ | |||
1 | import { action, observable, computed, reaction } from 'mobx'; | 1 | import { action, observable, computed, reaction } from 'mobx'; |
2 | import { theme, ThemeType } from '@meetfranz/theme'; | ||
3 | import { nativeTheme, systemPreferences } from '@electron/remote'; | 2 | import { nativeTheme, systemPreferences } from '@electron/remote'; |
4 | 3 | ||
4 | import { theme, ThemeType } from '../themes'; | ||
5 | import Store from './lib/Store'; | 5 | import Store from './lib/Store'; |
6 | import { isMac, isWindows } from '../environment'; | 6 | import { isMac, isWindows } from '../environment'; |
7 | 7 | ||
diff --git a/src/stores/UserStore.js b/src/stores/UserStore.js index 9f222d2d3..9a5d8cb30 100644 --- a/src/stores/UserStore.js +++ b/src/stores/UserStore.js | |||
@@ -273,13 +273,11 @@ export default class UserStore extends Store { | |||
273 | 273 | ||
274 | // Install recipes | 274 | // Install recipes |
275 | for (const recipe of recipes) { | 275 | for (const recipe of recipes) { |
276 | // eslint-disable-line no-unused-vars | ||
277 | // eslint-disable-next-line no-await-in-loop | 276 | // eslint-disable-next-line no-await-in-loop |
278 | await this.stores.recipes._install({ recipeId: recipe }); | 277 | await this.stores.recipes._install({ recipeId: recipe }); |
279 | } | 278 | } |
280 | 279 | ||
281 | for (const service of services) { | 280 | for (const service of services) { |
282 | // eslint-disable-line no-unused-vars | ||
283 | this.actions.service.createFromLegacyService({ | 281 | this.actions.service.createFromLegacyService({ |
284 | data: service, | 282 | data: service, |
285 | }); | 283 | }); |
diff --git a/src/stores/index.ts b/src/stores/index.ts index 1760ddfa2..6ad898d85 100644 --- a/src/stores/index.ts +++ b/src/stores/index.ts | |||
@@ -6,7 +6,6 @@ import ServicesStore from './ServicesStore'; | |||
6 | import RecipesStore from './RecipesStore'; | 6 | import RecipesStore from './RecipesStore'; |
7 | import RecipePreviewsStore from './RecipePreviewsStore'; | 7 | import RecipePreviewsStore from './RecipePreviewsStore'; |
8 | import UIStore from './UIStore'; | 8 | import UIStore from './UIStore'; |
9 | import NewsStore from './NewsStore'; | ||
10 | import RequestStore from './RequestStore'; | 9 | import RequestStore from './RequestStore'; |
11 | import GlobalErrorStore from './GlobalErrorStore'; | 10 | import GlobalErrorStore from './GlobalErrorStore'; |
12 | import { workspaceStore } from '../features/workspaces'; | 11 | import { workspaceStore } from '../features/workspaces'; |
@@ -25,7 +24,6 @@ export default (api, actions, router) => { | |||
25 | recipes: new RecipesStore(stores, api, actions), | 24 | recipes: new RecipesStore(stores, api, actions), |
26 | recipePreviews: new RecipePreviewsStore(stores, api, actions), | 25 | recipePreviews: new RecipePreviewsStore(stores, api, actions), |
27 | ui: new UIStore(stores, api, actions), | 26 | ui: new UIStore(stores, api, actions), |
28 | news: new NewsStore(stores, api, actions), | ||
29 | requests: new RequestStore(stores, api, actions), | 27 | requests: new RequestStore(stores, api, actions), |
30 | globalError: new GlobalErrorStore(stores, api, actions), | 28 | globalError: new GlobalErrorStore(stores, api, actions), |
31 | workspaces: workspaceStore, | 29 | workspaces: workspaceStore, |
diff --git a/src/stores/lib/Reaction.js b/src/stores/lib/Reaction.ts index 7e1bc685e..0ca24a6fa 100644 --- a/src/stores/lib/Reaction.js +++ b/src/stores/lib/Reaction.ts | |||
@@ -20,12 +20,10 @@ export default class Reaction { | |||
20 | 20 | ||
21 | stop() { | 21 | stop() { |
22 | if (this.isRunning) { | 22 | if (this.isRunning) { |
23 | this.dispose(); | 23 | this.dispose?.(); |
24 | this.isRunning = false; | 24 | this.isRunning = false; |
25 | } | 25 | } |
26 | } | 26 | } |
27 | } | 27 | } |
28 | 28 | ||
29 | export const createReactions = (reactions) => ( | 29 | export const createReactions = reactions => reactions.map(r => new Reaction(r)); |
30 | reactions.map((r) => new Reaction(r)) | ||
31 | ); | ||
diff --git a/src/stores/lib/Request.js b/src/stores/lib/Request.js index 39f32729a..65871ea17 100644 --- a/src/stores/lib/Request.js +++ b/src/stores/lib/Request.js | |||
@@ -38,42 +38,56 @@ export default class Request { | |||
38 | if (this._isWaitingForResponse) return this; | 38 | if (this._isWaitingForResponse) return this; |
39 | 39 | ||
40 | if (!this._api[this._method]) { | 40 | if (!this._api[this._method]) { |
41 | throw new Error(`Missing method <${this._method}> on api object:`, this._api); | 41 | throw new Error( |
42 | `Missing method <${this._method}> on api object:`, | ||
43 | this._api, | ||
44 | ); | ||
42 | } | 45 | } |
43 | 46 | ||
44 | // This timeout is necessary to avoid warnings from mobx | 47 | // This timeout is necessary to avoid warnings from mobx |
45 | // regarding triggering actions as side-effect of getters | 48 | // regarding triggering actions as side-effect of getters |
46 | setTimeout(action(() => { | 49 | setTimeout( |
47 | this.isExecuting = true; | 50 | action(() => { |
48 | }), 0); | 51 | this.isExecuting = true; |
52 | }), | ||
53 | 0, | ||
54 | ); | ||
49 | 55 | ||
50 | // Issue api call & save it as promise that is handled to update the results of the operation | 56 | // Issue api call & save it as promise that is handled to update the results of the operation |
51 | this._promise = new Promise((resolve, reject) => { | 57 | this._promise = new Promise((resolve, reject) => { |
52 | this._api[this._method](...callArgs) | 58 | this._api[this._method](...callArgs) |
53 | .then((result) => { | 59 | .then(result => { |
54 | setTimeout(action(() => { | 60 | setTimeout( |
55 | this.result = result; | 61 | action(() => { |
56 | if (this._currentApiCall) this._currentApiCall.result = result; | 62 | this.result = result; |
57 | this.isExecuting = false; | 63 | if (this._currentApiCall) this._currentApiCall.result = result; |
58 | this.isError = false; | 64 | this.isExecuting = false; |
59 | this.wasExecuted = true; | 65 | this.isError = false; |
60 | this._isWaitingForResponse = false; | 66 | this.wasExecuted = true; |
61 | this._triggerHooks(); | 67 | this._isWaitingForResponse = false; |
62 | resolve(result); | 68 | this._triggerHooks(); |
63 | }), 1); | 69 | resolve(result); |
70 | }), | ||
71 | 1, | ||
72 | ); | ||
64 | return result; | 73 | return result; |
65 | }) | 74 | }) |
66 | .catch(action((error) => { | 75 | .catch( |
67 | setTimeout(action(() => { | 76 | action(error => { |
68 | this.error = error; | 77 | setTimeout( |
69 | this.isExecuting = false; | 78 | action(() => { |
70 | this.isError = true; | 79 | this.error = error; |
71 | this.wasExecuted = true; | 80 | this.isExecuting = false; |
72 | this._isWaitingForResponse = false; | 81 | this.isError = true; |
73 | this._triggerHooks(); | 82 | this.wasExecuted = true; |
74 | reject(error); | 83 | this._isWaitingForResponse = false; |
75 | }), 1); | 84 | this._triggerHooks(); |
76 | })); | 85 | reject(error); |
86 | }), | ||
87 | 1, | ||
88 | ); | ||
89 | }), | ||
90 | ); | ||
77 | }); | 91 | }); |
78 | 92 | ||
79 | this._isWaitingForResponse = true; | 93 | this._isWaitingForResponse = true; |
@@ -89,7 +103,11 @@ export default class Request { | |||
89 | retry = () => this.reload(); | 103 | retry = () => this.reload(); |
90 | 104 | ||
91 | isExecutingWithArgs(...args) { | 105 | isExecutingWithArgs(...args) { |
92 | return this.isExecuting && this._currentApiCall && isEqual(this._currentApiCall.args, args); | 106 | return ( |
107 | this.isExecuting && | ||
108 | this._currentApiCall && | ||
109 | isEqual(this._currentApiCall.args, args) | ||
110 | ); | ||
93 | } | 111 | } |
94 | 112 | ||
95 | @computed get isExecutingFirstTime() { | 113 | @computed get isExecutingFirstTime() { |
@@ -97,12 +115,18 @@ export default class Request { | |||
97 | } | 115 | } |
98 | 116 | ||
99 | then(...args) { | 117 | then(...args) { |
100 | if (!this._promise) throw new Error('You have to call Request::execute before you can access it as promise'); | 118 | if (!this._promise) |
119 | throw new Error( | ||
120 | 'You have to call Request::execute before you can access it as promise', | ||
121 | ); | ||
101 | return this._promise.then(...args); | 122 | return this._promise.then(...args); |
102 | } | 123 | } |
103 | 124 | ||
104 | catch(...args) { | 125 | catch(...args) { |
105 | if (!this._promise) throw new Error('You have to call Request::execute before you can access it as promise'); | 126 | if (!this._promise) |
127 | throw new Error( | ||
128 | 'You have to call Request::execute before you can access it as promise', | ||
129 | ); | ||
106 | return this._promise.catch(...args); | 130 | return this._promise.catch(...args); |
107 | } | 131 | } |
108 | 132 | ||
diff --git a/src/stores/lib/Store.js b/src/stores/lib/Store.js index b39070ce8..a867c3a46 100644 --- a/src/stores/lib/Store.js +++ b/src/stores/lib/Store.js | |||
@@ -28,7 +28,8 @@ export default class Store { | |||
28 | } | 28 | } |
29 | 29 | ||
30 | registerReactions(reactions) { | 30 | registerReactions(reactions) { |
31 | for (const reaction of reactions) this._reactions.push(new Reaction(reaction)); | 31 | for (const reaction of reactions) |
32 | this._reactions.push(new Reaction(reaction)); | ||
32 | } | 33 | } |
33 | 34 | ||
34 | setup() {} | 35 | setup() {} |
diff --git a/src/themes/IStyleTypes.ts b/src/themes/IStyleTypes.ts new file mode 100644 index 000000000..48f52daf2 --- /dev/null +++ b/src/themes/IStyleTypes.ts | |||
@@ -0,0 +1,7 @@ | |||
1 | export interface IStyleTypes { | ||
2 | [index: string]: { | ||
3 | accent: string; | ||
4 | contrast: string; | ||
5 | border?: string; | ||
6 | }; | ||
7 | } | ||
diff --git a/src/themes/dark/index.ts b/src/themes/dark/index.ts new file mode 100644 index 000000000..aa132c743 --- /dev/null +++ b/src/themes/dark/index.ts | |||
@@ -0,0 +1,171 @@ | |||
1 | import color from 'color'; | ||
2 | import { cloneDeep, merge } from 'lodash'; | ||
3 | |||
4 | import makeDefaultThemeConfig from '../default'; | ||
5 | import * as legacyStyles from '../legacy'; | ||
6 | |||
7 | export default (brandPrimary: string) => { | ||
8 | const defaultStyles = makeDefaultThemeConfig(brandPrimary); | ||
9 | let brandPrimaryColor = color(legacyStyles.themeBrandPrimary); | ||
10 | try { | ||
11 | brandPrimaryColor = color(defaultStyles.brandPrimary); | ||
12 | } catch { | ||
13 | // Ignore invalid color and fall back to default. | ||
14 | } | ||
15 | |||
16 | const colorBackground = legacyStyles.darkThemeGrayDarkest; | ||
17 | const colorText = legacyStyles.darkThemeTextColor; | ||
18 | const inputColor = legacyStyles.darkThemeGrayLightest; | ||
19 | const inputBackground = legacyStyles.themeGrayDark; | ||
20 | const inputBorder = `1px solid ${legacyStyles.darkThemeGrayLight}`; | ||
21 | const inputPrefixColor = color(legacyStyles.darkThemeGrayLighter) | ||
22 | .lighten(0.3) | ||
23 | .hex(); | ||
24 | const buttonSecondaryTextColor = legacyStyles.darkThemeTextColor; | ||
25 | const selectColor = inputColor; | ||
26 | const drawerBg = color(colorBackground).lighten(0.3).hex(); | ||
27 | |||
28 | const services = merge({}, defaultStyles.services, { | ||
29 | listItems: { | ||
30 | borderColor: legacyStyles.darkThemeGrayDarker, | ||
31 | hoverBgColor: legacyStyles.darkThemeGrayDarker, | ||
32 | disabled: { | ||
33 | color: legacyStyles.darkThemeGray, | ||
34 | }, | ||
35 | }, | ||
36 | }); | ||
37 | |||
38 | return { | ||
39 | ...defaultStyles, | ||
40 | |||
41 | colorBackground, | ||
42 | colorContentBackground: legacyStyles.darkThemeGrayDarkest, | ||
43 | colorBackgroundSubscriptionContainer: legacyStyles.themeBrandInfo, | ||
44 | |||
45 | colorHeadline: legacyStyles.darkThemeTextColor, | ||
46 | colorText: legacyStyles.darkThemeTextColor, | ||
47 | |||
48 | defaultContentBorder: legacyStyles.themeGrayDark, | ||
49 | |||
50 | // Loader | ||
51 | colorFullscreenLoaderSpinner: '#FFF', | ||
52 | colorWebviewLoaderBackground: color(legacyStyles.darkThemeGrayDarkest) | ||
53 | .alpha(0.5) | ||
54 | .rgb() | ||
55 | .string(), | ||
56 | |||
57 | // Input | ||
58 | labelColor: legacyStyles.darkThemeTextColor, | ||
59 | inputColor, | ||
60 | inputBackground, | ||
61 | inputBorder, | ||
62 | inputPrefixColor, | ||
63 | inputPrefixBackground: legacyStyles.darkThemeGray, | ||
64 | inputDisabledOpacity: 0.5, | ||
65 | inputScorePasswordBackground: legacyStyles.darkThemeGrayDark, | ||
66 | inputModifierColor: color(legacyStyles.darkThemeGrayLighter) | ||
67 | .lighten(0.3) | ||
68 | .hex(), | ||
69 | inputPlaceholderColor: color(legacyStyles.darkThemeGrayLighter) | ||
70 | .darken(0.1) | ||
71 | .hex(), | ||
72 | |||
73 | // Toggle | ||
74 | toggleBackground: legacyStyles.darkThemeGray, | ||
75 | toggleButton: legacyStyles.darkThemeGrayLighter, | ||
76 | |||
77 | // Button | ||
78 | buttonPrimaryTextColor: legacyStyles.darkThemeTextColor, | ||
79 | |||
80 | buttonSecondaryBackground: legacyStyles.darkThemeGrayLighter, | ||
81 | buttonSecondaryTextColor, | ||
82 | |||
83 | buttonDangerTextColor: legacyStyles.darkThemeTextColor, | ||
84 | |||
85 | buttonWarningTextColor: legacyStyles.darkThemeTextColor, | ||
86 | |||
87 | buttonLoaderColor: { | ||
88 | primary: '#FFF', | ||
89 | secondary: buttonSecondaryTextColor, | ||
90 | success: '#FFF', | ||
91 | warning: '#FFF', | ||
92 | danger: '#FFF', | ||
93 | inverted: defaultStyles.brandPrimary, | ||
94 | }, | ||
95 | |||
96 | // Select | ||
97 | selectBackground: inputBackground, | ||
98 | selectBorder: inputBorder, | ||
99 | selectColor, | ||
100 | selectToggleColor: inputPrefixColor, | ||
101 | selectPopupBackground: legacyStyles.darkThemeGrayLight, | ||
102 | selectOptionColor: '#FFF', | ||
103 | selectOptionBorder: `1px solid ${color(legacyStyles.darkThemeGrayLight) | ||
104 | .darken(0.2) | ||
105 | .hex()}`, | ||
106 | selectOptionItemHover: color(legacyStyles.darkThemeGrayLight) | ||
107 | .darken(0.2) | ||
108 | .hex(), | ||
109 | selectOptionItemHoverColor: selectColor, | ||
110 | selectSearchColor: inputBackground, | ||
111 | |||
112 | // Modal | ||
113 | colorModalOverlayBackground: color(legacyStyles.darkThemeBlack) | ||
114 | .alpha(0.9) | ||
115 | .rgb() | ||
116 | .string(), | ||
117 | colorModalBackground: legacyStyles.darkThemeGrayDark, | ||
118 | |||
119 | // Services | ||
120 | services, | ||
121 | |||
122 | // Service Icon | ||
123 | serviceIcon: merge({}, defaultStyles.serviceIcon, { | ||
124 | isCustom: { | ||
125 | border: `1px solid ${legacyStyles.darkThemeGrayDark}`, | ||
126 | }, | ||
127 | }), | ||
128 | |||
129 | // Workspaces | ||
130 | workspaces: merge({}, defaultStyles.workspaces, { | ||
131 | settings: { | ||
132 | listItems: cloneDeep(services.listItems), | ||
133 | }, | ||
134 | drawer: { | ||
135 | background: drawerBg, | ||
136 | addButton: { | ||
137 | color: legacyStyles.darkThemeGrayLighter, | ||
138 | hoverColor: legacyStyles.darkThemeGraySmoke, | ||
139 | }, | ||
140 | listItem: { | ||
141 | border: color(drawerBg).lighten(0.2).hex(), | ||
142 | hoverBackground: color(drawerBg).lighten(0.2).hex(), | ||
143 | activeBackground: defaultStyles.brandPrimary, | ||
144 | name: { | ||
145 | color: colorText, | ||
146 | activeColor: 'white', | ||
147 | }, | ||
148 | services: { | ||
149 | color: color(colorText).darken(0.5).hex(), | ||
150 | active: brandPrimaryColor.lighten(0.5).hex(), | ||
151 | }, | ||
152 | }, | ||
153 | }, | ||
154 | }), | ||
155 | |||
156 | // Todos | ||
157 | todos: merge({}, defaultStyles.todos, { | ||
158 | todosLayer: { | ||
159 | borderLeftColor: legacyStyles.darkThemeGrayDarker, | ||
160 | }, | ||
161 | toggleButton: { | ||
162 | background: defaultStyles.styleTypes.primary.accent, | ||
163 | textColor: defaultStyles.styleTypes.primary.contrast, | ||
164 | shadowColor: 'rgba(0, 0, 0, 0.2)', | ||
165 | }, | ||
166 | dragIndicator: { | ||
167 | background: legacyStyles.themeGrayLight, | ||
168 | }, | ||
169 | }), | ||
170 | }; | ||
171 | }; | ||
diff --git a/src/themes/default/index.ts b/src/themes/default/index.ts new file mode 100644 index 000000000..80bcba766 --- /dev/null +++ b/src/themes/default/index.ts | |||
@@ -0,0 +1,251 @@ | |||
1 | import color from 'color'; | ||
2 | import { cloneDeep } from 'lodash'; | ||
3 | |||
4 | import * as legacyStyles from '../legacy'; | ||
5 | import type { IStyleTypes } from '../IStyleTypes'; | ||
6 | |||
7 | export default (brandPrimary: string) => { | ||
8 | const brandSuccess = '#5cb85c'; | ||
9 | const brandInfo = '#5bc0de'; | ||
10 | const brandWarning = '#FF9F00'; | ||
11 | const brandDanger = '#d9534f'; | ||
12 | const uiFontSize = 14; | ||
13 | const colorBackground = legacyStyles.themeGrayLighter; | ||
14 | const colorContentBackground = '#FFFFFF'; | ||
15 | const colorText = legacyStyles.themeTextColor; | ||
16 | const inputColor = legacyStyles.themeGray; | ||
17 | const inputBackground = legacyStyles.themeGrayLightest; | ||
18 | const inputHeight = 40; | ||
19 | const inputBorder = `1px solid ${legacyStyles.themeGrayLighter}`; | ||
20 | const inputPrefixColor = legacyStyles.themeGrayLight; | ||
21 | const inputDisabledOpacity = 0.5; | ||
22 | const buttonSecondaryTextColor = legacyStyles.themeGray; | ||
23 | const selectColor = inputColor; | ||
24 | const drawerBg = color(colorBackground).lighten(0.1).hex(); | ||
25 | |||
26 | const styleTypes: IStyleTypes = { | ||
27 | primary: { | ||
28 | accent: brandPrimary, | ||
29 | contrast: '#FFF', | ||
30 | }, | ||
31 | secondary: { | ||
32 | accent: legacyStyles.themeGrayLighter, | ||
33 | contrast: legacyStyles.themeGray, | ||
34 | }, | ||
35 | success: { | ||
36 | accent: brandSuccess, | ||
37 | contrast: '#FFF', | ||
38 | }, | ||
39 | warning: { | ||
40 | accent: brandWarning, | ||
41 | contrast: '#FFF', | ||
42 | }, | ||
43 | danger: { | ||
44 | accent: brandDanger, | ||
45 | contrast: '#FFF', | ||
46 | }, | ||
47 | inverted: { | ||
48 | accent: 'none', | ||
49 | contrast: brandPrimary, | ||
50 | border: `1px solid ${brandPrimary}`, | ||
51 | }, | ||
52 | }; | ||
53 | |||
54 | const services = { | ||
55 | listItems: { | ||
56 | padding: 10, | ||
57 | height: 57, | ||
58 | borderColor: legacyStyles.themeGrayLightest, | ||
59 | hoverBgColor: legacyStyles.themeGrayLightest, | ||
60 | disabled: { | ||
61 | color: legacyStyles.themeGrayLight, | ||
62 | }, | ||
63 | }, | ||
64 | }; | ||
65 | |||
66 | return { | ||
67 | brandPrimary, | ||
68 | brandSuccess, | ||
69 | brandInfo, | ||
70 | brandWarning, | ||
71 | brandDanger, | ||
72 | |||
73 | uiFontSize, | ||
74 | |||
75 | borderRadius: legacyStyles.themeBorderRadius, | ||
76 | borderRadiusSmall: legacyStyles.themeBorderRadiusSmall, | ||
77 | |||
78 | colorBackground, | ||
79 | |||
80 | colorContentBackground, | ||
81 | colorHeadline: legacyStyles.themeGrayDark, | ||
82 | |||
83 | colorText, | ||
84 | |||
85 | defaultContentBorder: color(legacyStyles.themeGrayLighter) | ||
86 | .darken(0.1) | ||
87 | .rgb() | ||
88 | .string(), | ||
89 | |||
90 | // Subscription Container Component | ||
91 | colorSubscriptionContainerBackground: 'none', | ||
92 | colorSubscriptionContainerBorder: `1px solid ${brandPrimary}`, | ||
93 | colorSubscriptionContainerTitle: brandPrimary, | ||
94 | colorSubscriptionContainerActionButtonBackground: brandPrimary, | ||
95 | colorSubscriptionContainerActionButtonColor: '#FFF', | ||
96 | |||
97 | // Loader | ||
98 | colorAppLoaderSpinner: '#FFF', | ||
99 | colorFullscreenLoaderSpinner: legacyStyles.themeGrayDark, | ||
100 | colorWebviewLoaderBackground: color(legacyStyles.themeGrayLighter) | ||
101 | .alpha(0.8) | ||
102 | .rgb() | ||
103 | .string(), | ||
104 | |||
105 | // Input | ||
106 | labelColor: legacyStyles.themeGrayLight, | ||
107 | inputColor, | ||
108 | inputHeight, | ||
109 | inputBackground, | ||
110 | inputBorder, | ||
111 | inputModifierColor: legacyStyles.themeGrayLight, | ||
112 | inputPlaceholderColor: color(legacyStyles.themeGrayLight) | ||
113 | .lighten(0.3) | ||
114 | .hex(), | ||
115 | inputPrefixColor, | ||
116 | inputPrefixBackground: legacyStyles.themeGrayLighter, | ||
117 | inputDisabledOpacity, | ||
118 | inputScorePasswordBackground: legacyStyles.themeGrayLighter, | ||
119 | |||
120 | // Toggle | ||
121 | toggleBackground: legacyStyles.themeGrayLighter, | ||
122 | toggleButton: legacyStyles.themeGrayLight, | ||
123 | toggleButtonActive: brandPrimary, | ||
124 | toggleWidth: 40, | ||
125 | toggleHeight: 14, | ||
126 | |||
127 | // Style Types | ||
128 | styleTypes, | ||
129 | |||
130 | // Button | ||
131 | buttonPrimaryBackground: brandPrimary, | ||
132 | buttonPrimaryTextColor: '#FFF', | ||
133 | |||
134 | buttonSecondaryBackground: legacyStyles.themeGrayLighter, | ||
135 | buttonSecondaryTextColor, | ||
136 | |||
137 | buttonSuccessBackground: brandSuccess, | ||
138 | buttonSuccessTextColor: '#FFF', | ||
139 | |||
140 | buttonDangerBackground: brandDanger, | ||
141 | buttonDangerTextColor: '#FFF', | ||
142 | |||
143 | buttonWarningBackground: brandWarning, | ||
144 | buttonWarningTextColor: '#FFF', | ||
145 | |||
146 | buttonInvertedBackground: 'none', | ||
147 | buttonInvertedTextColor: brandPrimary, | ||
148 | buttonInvertedBorder: `1px solid ${brandPrimary}`, | ||
149 | |||
150 | buttonHeight: inputHeight, | ||
151 | |||
152 | buttonLoaderColor: { | ||
153 | primary: '#FFF', | ||
154 | secondary: buttonSecondaryTextColor, | ||
155 | success: '#FFF', | ||
156 | warning: '#FFF', | ||
157 | danger: '#FFF', | ||
158 | inverted: brandPrimary, | ||
159 | }, | ||
160 | |||
161 | // Select | ||
162 | selectBackground: inputBackground, | ||
163 | selectBorder: inputBorder, | ||
164 | selectHeight: inputHeight, | ||
165 | selectColor, | ||
166 | selectToggleColor: inputPrefixColor, | ||
167 | selectPopupBackground: '#FFF', | ||
168 | selectOptionColor: inputColor, | ||
169 | selectOptionBorder: `1px solid ${legacyStyles.themeGrayLightest}`, | ||
170 | selectOptionItemHover: legacyStyles.themeGrayLighter, | ||
171 | selectOptionItemHoverColor: selectColor, | ||
172 | selectOptionItemActive: brandPrimary, | ||
173 | selectOptionItemActiveColor: '#FFF', | ||
174 | selectSearchBackground: legacyStyles.themeGrayLighter, | ||
175 | selectSearchColor: inputColor, | ||
176 | selectDisabledOpacity: inputDisabledOpacity, | ||
177 | |||
178 | // Badge | ||
179 | badgeFontSize: uiFontSize - 2, | ||
180 | badgeBorderRadius: 50, | ||
181 | |||
182 | // Modal | ||
183 | colorModalOverlayBackground: color('#000').alpha(0.8).rgb().string(), | ||
184 | colorModalBackground: colorContentBackground, | ||
185 | |||
186 | // Services | ||
187 | services, | ||
188 | |||
189 | // Service Icon | ||
190 | serviceIcon: { | ||
191 | width: 35, | ||
192 | isCustom: { | ||
193 | border: `1px solid ${legacyStyles.themeGrayLighter}`, | ||
194 | borderRadius: legacyStyles.themeBorderRadius, | ||
195 | width: 37, | ||
196 | }, | ||
197 | }, | ||
198 | |||
199 | // Workspaces | ||
200 | workspaces: { | ||
201 | settings: { | ||
202 | listItems: cloneDeep(services.listItems), | ||
203 | }, | ||
204 | drawer: { | ||
205 | width: 300, | ||
206 | padding: 20, | ||
207 | background: drawerBg, | ||
208 | buttons: { | ||
209 | color: color(legacyStyles.themeGrayLight).lighten(0.1).hex(), | ||
210 | hoverColor: legacyStyles.themeGrayLight, | ||
211 | }, | ||
212 | listItem: { | ||
213 | hoverBackground: color(drawerBg).darken(0.01).hex(), | ||
214 | activeBackground: legacyStyles.themeGrayLightest, | ||
215 | border: color(drawerBg).darken(0.05).hex(), | ||
216 | name: { | ||
217 | color: colorText, | ||
218 | activeColor: colorText, | ||
219 | }, | ||
220 | services: { | ||
221 | color: color(colorText).lighten(1.5).hex(), | ||
222 | active: color(colorText).lighten(1.5).hex(), | ||
223 | }, | ||
224 | }, | ||
225 | }, | ||
226 | switchingIndicator: { | ||
227 | spinnerColor: 'white', | ||
228 | }, | ||
229 | }, | ||
230 | |||
231 | // Todos | ||
232 | todos: { | ||
233 | todosLayer: { | ||
234 | borderLeftColor: color(legacyStyles.themeGrayLighter).darken(0.1).hex(), | ||
235 | }, | ||
236 | toggleButton: { | ||
237 | background: styleTypes.primary.accent, | ||
238 | textColor: styleTypes.primary.contrast, | ||
239 | shadowColor: 'rgba(0, 0, 0, 0.2)', | ||
240 | }, | ||
241 | dragIndicator: { | ||
242 | background: legacyStyles.themeGrayLight, | ||
243 | }, | ||
244 | resizeHandler: { | ||
245 | backgroundHover: styleTypes.primary.accent, | ||
246 | }, | ||
247 | }, | ||
248 | |||
249 | legacyStyles, | ||
250 | }; | ||
251 | }; | ||
diff --git a/src/themes/index.ts b/src/themes/index.ts new file mode 100644 index 000000000..27be4d04b --- /dev/null +++ b/src/themes/index.ts | |||
@@ -0,0 +1,21 @@ | |||
1 | import makeDarkThemeConfig from './dark'; | ||
2 | import makeDefaultThemeConfig from './default'; | ||
3 | import { themeBrandPrimary } from './legacy'; | ||
4 | |||
5 | export enum ThemeType { | ||
6 | default = 'default', | ||
7 | dark = 'dark', | ||
8 | } | ||
9 | |||
10 | export function theme( | ||
11 | themeId: ThemeType, | ||
12 | brandColor: string = themeBrandPrimary, | ||
13 | ) { | ||
14 | return themeId === ThemeType.dark | ||
15 | ? makeDarkThemeConfig(brandColor) | ||
16 | : makeDefaultThemeConfig(brandColor); | ||
17 | } | ||
18 | |||
19 | const defaultThemeConfigWithDefaultAccentColor = makeDefaultThemeConfig(themeBrandPrimary); | ||
20 | |||
21 | export type Theme = typeof defaultThemeConfigWithDefaultAccentColor; | ||
diff --git a/src/themes/legacy/index.ts b/src/themes/legacy/index.ts new file mode 100644 index 000000000..c6105a4e2 --- /dev/null +++ b/src/themes/legacy/index.ts | |||
@@ -0,0 +1,40 @@ | |||
1 | import { DEFAULT_ACCENT_COLOR } from '../../config'; | ||
2 | |||
3 | /* legacy config, injected into sass */ | ||
4 | export const themeBrandPrimary = DEFAULT_ACCENT_COLOR; | ||
5 | export const themeBrandSuccess = '#5cb85c'; | ||
6 | export const themeBrandInfo = '#5bc0de'; | ||
7 | export const themeBrandWarning = '#FF9F00'; | ||
8 | export const themeBrandDanger = '#d9534f'; | ||
9 | |||
10 | export const themeGrayDark = '#373a3c'; | ||
11 | export const themeGray = '#55595c'; | ||
12 | export const themeGrayLight = '#818a91'; | ||
13 | export const themeGrayLighter = '#eceeef'; | ||
14 | export const themeGrayLightest = '#f7f7f9'; | ||
15 | |||
16 | export const themeBorderRadius = '6px'; | ||
17 | export const themeBorderRadiusSmall = '3px'; | ||
18 | |||
19 | export const themeSidebarWidth = '68px'; | ||
20 | |||
21 | export const themeTextColor = themeGrayDark; | ||
22 | |||
23 | export const themeTransitionTime = '.5s'; | ||
24 | |||
25 | export const themeInsetShadow = 'inset 0 2px 5px rgba(0, 0, 0, .03)'; | ||
26 | |||
27 | export const darkThemeBlack = '#1A1A1A'; | ||
28 | |||
29 | export const darkThemeGrayDarkest = '#1E1E1E'; | ||
30 | export const darkThemeGrayDarker = '#2D2F31'; | ||
31 | export const darkThemeGrayDark = '#383A3B'; | ||
32 | |||
33 | export const darkThemeGray = '#47494B'; | ||
34 | |||
35 | export const darkThemeGrayLight = '#515355'; | ||
36 | export const darkThemeGrayLighter = '#8a8b8b'; | ||
37 | export const darkThemeGrayLightest = '#FFFFFF'; | ||
38 | |||
39 | export const darkThemeGraySmoke = '#CED0D1'; | ||
40 | export const darkThemeTextColor = '#FFFFFF'; | ||
diff --git a/src/webview/badge.ts b/src/webview/badge.ts index 8e8b66c0c..fb696723d 100644 --- a/src/webview/badge.ts +++ b/src/webview/badge.ts | |||
@@ -3,7 +3,7 @@ import { ipcRenderer } from 'electron'; | |||
3 | const debug = require('debug')('Ferdi:Plugin:BadgeHandler'); | 3 | const debug = require('debug')('Ferdi:Plugin:BadgeHandler'); |
4 | 4 | ||
5 | export class BadgeHandler { | 5 | export class BadgeHandler { |
6 | countCache: { direct: number; indirect: number; }; | 6 | countCache: { direct: number; indirect: number }; |
7 | 7 | ||
8 | constructor() { | 8 | constructor() { |
9 | this.countCache = { | 9 | this.countCache = { |
@@ -26,14 +26,19 @@ export class BadgeHandler { | |||
26 | return Math.max(adjustedNumber, 0); | 26 | return Math.max(adjustedNumber, 0); |
27 | } | 27 | } |
28 | 28 | ||
29 | setBadge(direct: string | number | undefined | null, indirect: string | number | undefined | null) { | 29 | setBadge( |
30 | direct: string | number | undefined | null, | ||
31 | indirect: string | number | undefined | null, | ||
32 | ) { | ||
30 | const count = { | 33 | const count = { |
31 | direct: this.safeParseInt(direct), | 34 | direct: this.safeParseInt(direct), |
32 | indirect: this.safeParseInt(indirect), | 35 | indirect: this.safeParseInt(indirect), |
33 | }; | 36 | }; |
34 | 37 | ||
35 | if (this.countCache.direct.toString() === count.direct.toString() | 38 | if ( |
36 | && this.countCache.indirect.toString() === count.indirect.toString()) { | 39 | this.countCache.direct.toString() === count.direct.toString() && |
40 | this.countCache.indirect.toString() === count.indirect.toString() | ||
41 | ) { | ||
37 | return; | 42 | return; |
38 | } | 43 | } |
39 | 44 | ||
diff --git a/src/webview/contextMenu.js b/src/webview/contextMenu.js deleted file mode 100644 index 567a2d470..000000000 --- a/src/webview/contextMenu.js +++ /dev/null | |||
@@ -1,20 +0,0 @@ | |||
1 | import { getCurrentWebContents } from '@electron/remote'; | ||
2 | import ContextMenuBuilder from './contextMenuBuilder'; | ||
3 | |||
4 | const webContents = getCurrentWebContents(); | ||
5 | |||
6 | export default async function setupContextMenu(isSpellcheckEnabled, getDefaultSpellcheckerLanguage, getSpellcheckerLanguage, getSearchEngine, getClipboardNotifications) { | ||
7 | const contextMenuBuilder = new ContextMenuBuilder( | ||
8 | webContents, | ||
9 | ); | ||
10 | |||
11 | webContents.on('context-menu', (e, props) => { | ||
12 | // TODO?: e.preventDefault(); | ||
13 | contextMenuBuilder.showPopupMenu( | ||
14 | { ...props, searchEngine: getSearchEngine(), clipboardNotifications: getClipboardNotifications() }, | ||
15 | isSpellcheckEnabled(), | ||
16 | getDefaultSpellcheckerLanguage(), | ||
17 | getSpellcheckerLanguage(), | ||
18 | ); | ||
19 | }); | ||
20 | } | ||
diff --git a/src/webview/contextMenu.ts b/src/webview/contextMenu.ts new file mode 100644 index 000000000..72f927ef4 --- /dev/null +++ b/src/webview/contextMenu.ts | |||
@@ -0,0 +1,29 @@ | |||
1 | import { getCurrentWebContents } from '@electron/remote'; | ||
2 | import { ContextMenuBuilder } from './contextMenuBuilder'; | ||
3 | |||
4 | const webContents = getCurrentWebContents(); | ||
5 | |||
6 | export default async function setupContextMenu( | ||
7 | isSpellcheckEnabled: () => void, | ||
8 | getDefaultSpellcheckerLanguage: () => void, | ||
9 | getSpellcheckerLanguage: () => void, | ||
10 | getSearchEngine: () => void, | ||
11 | getClipboardNotifications: () => void, | ||
12 | ) { | ||
13 | const contextMenuBuilder = new ContextMenuBuilder(webContents); | ||
14 | |||
15 | webContents.on('context-menu', (_e, props) => { | ||
16 | // TODO?: e.preventDefault(); | ||
17 | contextMenuBuilder.showPopupMenu( | ||
18 | { | ||
19 | ...props, | ||
20 | searchEngine: getSearchEngine(), | ||
21 | clipboardNotifications: getClipboardNotifications(), | ||
22 | }, | ||
23 | // @ts-expect-error Expected 1 arguments, but got 4. | ||
24 | isSpellcheckEnabled(), | ||
25 | getDefaultSpellcheckerLanguage(), | ||
26 | getSpellcheckerLanguage(), | ||
27 | ); | ||
28 | }); | ||
29 | } | ||
diff --git a/src/webview/contextMenuBuilder.js b/src/webview/contextMenuBuilder.ts index 938eade1e..7b8c6cb2d 100644 --- a/src/webview/contextMenuBuilder.js +++ b/src/webview/contextMenuBuilder.ts | |||
@@ -6,7 +6,7 @@ | |||
6 | * | 6 | * |
7 | * Source: https://github.com/electron-userland/electron-spellchecker/blob/master/src/context-menu-builder.js | 7 | * Source: https://github.com/electron-userland/electron-spellchecker/blob/master/src/context-menu-builder.js |
8 | */ | 8 | */ |
9 | // eslint-disable-next-line no-unused-vars | 9 | |
10 | import { clipboard, ipcRenderer, nativeImage, WebContents } from 'electron'; | 10 | import { clipboard, ipcRenderer, nativeImage, WebContents } from 'electron'; |
11 | import { Menu, MenuItem } from '@electron/remote'; | 11 | import { Menu, MenuItem } from '@electron/remote'; |
12 | import { cmdOrCtrlShortcutKey, isMac } from '../environment'; | 12 | import { cmdOrCtrlShortcutKey, isMac } from '../environment'; |
@@ -14,17 +14,38 @@ import { cmdOrCtrlShortcutKey, isMac } from '../environment'; | |||
14 | import { SEARCH_ENGINE_NAMES, SEARCH_ENGINE_URLS } from '../config'; | 14 | import { SEARCH_ENGINE_NAMES, SEARCH_ENGINE_URLS } from '../config'; |
15 | import { openExternalUrl } from '../helpers/url-helpers'; | 15 | import { openExternalUrl } from '../helpers/url-helpers'; |
16 | 16 | ||
17 | const { URL } = require('url'); | 17 | function matchesWord(string: string) { |
18 | |||
19 | function matchesWord(string) { | ||
20 | const regex = | 18 | const regex = |
21 | /[A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g; | 19 | /[A-Za-z\u00AA\u00B5\u00BA\u00C0-\u00D6\u00D8-\u00F6\u00F8-\u02C1\u02C6-\u02D1\u02E0-\u02E4\u02EC\u02EE\u0370-\u0374\u0376\u0377\u037A-\u037D\u0386\u0388-\u038A\u038C\u038E-\u03A1\u03A3-\u03F5\u03F7-\u0481\u048A-\u0527\u0531-\u0556\u0559\u0561-\u0587\u05D0-\u05EA\u05F0-\u05F2\u0620-\u064A\u066E\u066F\u0671-\u06D3\u06D5\u06E5\u06E6\u06EE\u06EF\u06FA-\u06FC\u06FF\u0710\u0712-\u072F\u074D-\u07A5\u07B1\u07CA-\u07EA\u07F4\u07F5\u07FA\u0800-\u0815\u081A\u0824\u0828\u0840-\u0858\u08A0\u08A2-\u08AC\u0904-\u0939\u093D\u0950\u0958-\u0961\u0971-\u0977\u0979-\u097F\u0985-\u098C\u098F\u0990\u0993-\u09A8\u09AA-\u09B0\u09B2\u09B6-\u09B9\u09BD\u09CE\u09DC\u09DD\u09DF-\u09E1\u09F0\u09F1\u0A05-\u0A0A\u0A0F\u0A10\u0A13-\u0A28\u0A2A-\u0A30\u0A32\u0A33\u0A35\u0A36\u0A38\u0A39\u0A59-\u0A5C\u0A5E\u0A72-\u0A74\u0A85-\u0A8D\u0A8F-\u0A91\u0A93-\u0AA8\u0AAA-\u0AB0\u0AB2\u0AB3\u0AB5-\u0AB9\u0ABD\u0AD0\u0AE0\u0AE1\u0B05-\u0B0C\u0B0F\u0B10\u0B13-\u0B28\u0B2A-\u0B30\u0B32\u0B33\u0B35-\u0B39\u0B3D\u0B5C\u0B5D\u0B5F-\u0B61\u0B71\u0B83\u0B85-\u0B8A\u0B8E-\u0B90\u0B92-\u0B95\u0B99\u0B9A\u0B9C\u0B9E\u0B9F\u0BA3\u0BA4\u0BA8-\u0BAA\u0BAE-\u0BB9\u0BD0\u0C05-\u0C0C\u0C0E-\u0C10\u0C12-\u0C28\u0C2A-\u0C33\u0C35-\u0C39\u0C3D\u0C58\u0C59\u0C60\u0C61\u0C85-\u0C8C\u0C8E-\u0C90\u0C92-\u0CA8\u0CAA-\u0CB3\u0CB5-\u0CB9\u0CBD\u0CDE\u0CE0\u0CE1\u0CF1\u0CF2\u0D05-\u0D0C\u0D0E-\u0D10\u0D12-\u0D3A\u0D3D\u0D4E\u0D60\u0D61\u0D7A-\u0D7F\u0D85-\u0D96\u0D9A-\u0DB1\u0DB3-\u0DBB\u0DBD\u0DC0-\u0DC6\u0E01-\u0E30\u0E32\u0E33\u0E40-\u0E46\u0E81\u0E82\u0E84\u0E87\u0E88\u0E8A\u0E8D\u0E94-\u0E97\u0E99-\u0E9F\u0EA1-\u0EA3\u0EA5\u0EA7\u0EAA\u0EAB\u0EAD-\u0EB0\u0EB2\u0EB3\u0EBD\u0EC0-\u0EC4\u0EC6\u0EDC-\u0EDF\u0F00\u0F40-\u0F47\u0F49-\u0F6C\u0F88-\u0F8C\u1000-\u102A\u103F\u1050-\u1055\u105A-\u105D\u1061\u1065\u1066\u106E-\u1070\u1075-\u1081\u108E\u10A0-\u10C5\u10C7\u10CD\u10D0-\u10FA\u10FC-\u1248\u124A-\u124D\u1250-\u1256\u1258\u125A-\u125D\u1260-\u1288\u128A-\u128D\u1290-\u12B0\u12B2-\u12B5\u12B8-\u12BE\u12C0\u12C2-\u12C5\u12C8-\u12D6\u12D8-\u1310\u1312-\u1315\u1318-\u135A\u1380-\u138F\u13A0-\u13F4\u1401-\u166C\u166F-\u167F\u1681-\u169A\u16A0-\u16EA\u1700-\u170C\u170E-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176C\u176E-\u1770\u1780-\u17B3\u17D7\u17DC\u1820-\u1877\u1880-\u18A8\u18AA\u18B0-\u18F5\u1900-\u191C\u1950-\u196D\u1970-\u1974\u1980-\u19AB\u19C1-\u19C7\u1A00-\u1A16\u1A20-\u1A54\u1AA7\u1B05-\u1B33\u1B45-\u1B4B\u1B83-\u1BA0\u1BAE\u1BAF\u1BBA-\u1BE5\u1C00-\u1C23\u1C4D-\u1C4F\u1C5A-\u1C7D\u1CE9-\u1CEC\u1CEE-\u1CF1\u1CF5\u1CF6\u1D00-\u1DBF\u1E00-\u1F15\u1F18-\u1F1D\u1F20-\u1F45\u1F48-\u1F4D\u1F50-\u1F57\u1F59\u1F5B\u1F5D\u1F5F-\u1F7D\u1F80-\u1FB4\u1FB6-\u1FBC\u1FBE\u1FC2-\u1FC4\u1FC6-\u1FCC\u1FD0-\u1FD3\u1FD6-\u1FDB\u1FE0-\u1FEC\u1FF2-\u1FF4\u1FF6-\u1FFC\u2071\u207F\u2090-\u209C\u2102\u2107\u210A-\u2113\u2115\u2119-\u211D\u2124\u2126\u2128\u212A-\u212D\u212F-\u2139\u213C-\u213F\u2145-\u2149\u214E\u2183\u2184\u2C00-\u2C2E\u2C30-\u2C5E\u2C60-\u2CE4\u2CEB-\u2CEE\u2CF2\u2CF3\u2D00-\u2D25\u2D27\u2D2D\u2D30-\u2D67\u2D6F\u2D80-\u2D96\u2DA0-\u2DA6\u2DA8-\u2DAE\u2DB0-\u2DB6\u2DB8-\u2DBE\u2DC0-\u2DC6\u2DC8-\u2DCE\u2DD0-\u2DD6\u2DD8-\u2DDE\u2E2F\u3005\u3006\u3031-\u3035\u303B\u303C\u3041-\u3096\u309D-\u309F\u30A1-\u30FA\u30FC-\u30FF\u3105-\u312D\u3131-\u318E\u31A0-\u31BA\u31F0-\u31FF\u3400-\u4DB5\u4E00-\u9FCC\uA000-\uA48C\uA4D0-\uA4FD\uA500-\uA60C\uA610-\uA61F\uA62A\uA62B\uA640-\uA66E\uA67F-\uA697\uA6A0-\uA6E5\uA717-\uA71F\uA722-\uA788\uA78B-\uA78E\uA790-\uA793\uA7A0-\uA7AA\uA7F8-\uA801\uA803-\uA805\uA807-\uA80A\uA80C-\uA822\uA840-\uA873\uA882-\uA8B3\uA8F2-\uA8F7\uA8FB\uA90A-\uA925\uA930-\uA946\uA960-\uA97C\uA984-\uA9B2\uA9CF\uAA00-\uAA28\uAA40-\uAA42\uAA44-\uAA4B\uAA60-\uAA76\uAA7A\uAA80-\uAAAF\uAAB1\uAAB5\uAAB6\uAAB9-\uAABD\uAAC0\uAAC2\uAADB-\uAADD\uAAE0-\uAAEA\uAAF2-\uAAF4\uAB01-\uAB06\uAB09-\uAB0E\uAB11-\uAB16\uAB20-\uAB26\uAB28-\uAB2E\uABC0-\uABE2\uAC00-\uD7A3\uD7B0-\uD7C6\uD7CB-\uD7FB\uF900-\uFA6D\uFA70-\uFAD9\uFB00-\uFB06\uFB13-\uFB17\uFB1D\uFB1F-\uFB28\uFB2A-\uFB36\uFB38-\uFB3C\uFB3E\uFB40\uFB41\uFB43\uFB44\uFB46-\uFBB1\uFBD3-\uFD3D\uFD50-\uFD8F\uFD92-\uFDC7\uFDF0-\uFDFB\uFE70-\uFE74\uFE76-\uFEFC\uFF21-\uFF3A\uFF41-\uFF5A\uFF66-\uFFBE\uFFC2-\uFFC7\uFFCA-\uFFCF\uFFD2-\uFFD7\uFFDA-\uFFDC]+/g; |
22 | 20 | ||
23 | return string.match(regex); | 21 | return string.match(regex); |
24 | } | 22 | } |
25 | 23 | ||
24 | interface ContextMenuStringTable { | ||
25 | lookUpDefinition: ({ word }: { word: string }) => string; | ||
26 | cut: () => string; | ||
27 | copy: () => string; | ||
28 | paste: () => string; | ||
29 | pasteAndMatchStyle: () => string; | ||
30 | searchWith: ({ searchEngine }: { searchEngine: string }) => string; | ||
31 | openLinkUrl: () => string; | ||
32 | openLinkInFerdiUrl: () => string; | ||
33 | openInBrowser: () => string; | ||
34 | copyLinkUrl: () => string; | ||
35 | copyImageUrl: () => string; | ||
36 | copyImage: () => string; | ||
37 | downloadImage: () => string; | ||
38 | addToDictionary: () => string; | ||
39 | goBack: () => string; | ||
40 | goForward: () => string; | ||
41 | copyPageUrl: () => string; | ||
42 | goToHomePage: () => string; | ||
43 | copyMail: () => string; | ||
44 | inspectElement: () => string; | ||
45 | } | ||
46 | |||
26 | // TODO: Need to externalize for i18n | 47 | // TODO: Need to externalize for i18n |
27 | const contextMenuStringTable = { | 48 | const contextMenuStringTable: ContextMenuStringTable = { |
28 | lookUpDefinition: ({ word }) => `Look Up "${word}"`, | 49 | lookUpDefinition: ({ word }) => `Look Up "${word}"`, |
29 | cut: () => 'Cut', | 50 | cut: () => 'Cut', |
30 | copy: () => 'Copy', | 51 | copy: () => 'Copy', |
@@ -54,16 +75,31 @@ const contextMenuStringTable = { | |||
54 | * which we use to generate the menu. We also use the spell-check information to | 75 | * which we use to generate the menu. We also use the spell-check information to |
55 | * generate suggestions. | 76 | * generate suggestions. |
56 | */ | 77 | */ |
57 | module.exports = class ContextMenuBuilder { | 78 | export class ContextMenuBuilder { |
79 | debugMode: boolean; | ||
80 | |||
81 | processMenu: ( | ||
82 | menu: Electron.CrossProcessExports.Menu, | ||
83 | ) => Electron.CrossProcessExports.Menu; | ||
84 | |||
85 | menu: Electron.CrossProcessExports.Menu | null; | ||
86 | |||
87 | stringTable: ContextMenuStringTable; | ||
88 | |||
89 | getWebContents: () => Electron.WebContents; | ||
90 | |||
58 | /** | 91 | /** |
59 | * Creates an instance of ContextMenuBuilder | 92 | * Creates an instance of ContextMenuBuilder |
60 | * | 93 | * |
61 | * @param {WebContents} webContents Current webContents | 94 | * @param webContents Current webContents |
62 | * @param {Boolean} debugMode If true, display the "Inspect Element" menu item. | 95 | * @param debugMode If true, display the "Inspect Element" menu item. |
63 | * @param {function} processMenu If passed, this method will be passed the menu to change | 96 | * @param processMenu If passed, this method will be passed the menu to change it prior to display. Signature: (menu, info) => menu |
64 | * it prior to display. Signature: (menu, info) => menu | ||
65 | */ | 97 | */ |
66 | constructor(webContents, debugMode = false, processMenu = m => m) { | 98 | constructor( |
99 | webContents: WebContents, | ||
100 | debugMode: boolean = false, | ||
101 | processMenu = (menu: Electron.CrossProcessExports.Menu) => menu, | ||
102 | ) { | ||
67 | this.debugMode = debugMode; | 103 | this.debugMode = debugMode; |
68 | this.processMenu = processMenu; | 104 | this.processMenu = processMenu; |
69 | this.menu = null; | 105 | this.menu = null; |
@@ -77,12 +113,11 @@ module.exports = class ContextMenuBuilder { | |||
77 | * allows to change string in runtime. All formatters are simply typeof () => string, except | 113 | * allows to change string in runtime. All formatters are simply typeof () => string, except |
78 | * lookUpDefinition provides word, ({word}) => string. | 114 | * lookUpDefinition provides word, ({word}) => string. |
79 | * | 115 | * |
80 | * @param {Object} stringTable The object contains string foramtter function for context menu. | 116 | * @param stringTable The object contains string formatter function for context menu. |
81 | * It is allowed to specify only certain menu string as necessary, which will makes other string | 117 | * It is allowed to specify only certain menu string as necessary, which will makes other string |
82 | * fall backs to default. | 118 | * fall backs to default. |
83 | * | ||
84 | */ | 119 | */ |
85 | setAlternateStringFormatter(stringTable) { | 120 | setAlternateStringFormatter(stringTable: ContextMenuStringTable) { |
86 | this.stringTable = Object.assign(this.stringTable, stringTable); | 121 | this.stringTable = Object.assign(this.stringTable, stringTable); |
87 | } | 122 | } |
88 | 123 | ||
@@ -90,12 +125,9 @@ module.exports = class ContextMenuBuilder { | |||
90 | * Shows a popup menu given the information returned from the context-menu | 125 | * Shows a popup menu given the information returned from the context-menu |
91 | * event. This is probably the only method you need to call in this class. | 126 | * event. This is probably the only method you need to call in this class. |
92 | * | 127 | * |
93 | * @param {Object} contextInfo The object returned from the 'context-menu' | 128 | * @param contextInfo The object returned from the 'context-menu' Electron event. |
94 | * Electron event. | ||
95 | * | ||
96 | * @return {Promise} Completion | ||
97 | */ | 129 | */ |
98 | async showPopupMenu(contextInfo) { | 130 | async showPopupMenu(contextInfo: Electron.ContextMenuParams): Promise<void> { |
99 | const menu = await this.buildMenuForElement(contextInfo); | 131 | const menu = await this.buildMenuForElement(contextInfo); |
100 | if (!menu) return; | 132 | if (!menu) return; |
101 | menu.popup(); | 133 | menu.popup(); |
@@ -105,10 +137,10 @@ module.exports = class ContextMenuBuilder { | |||
105 | * Builds a context menu specific to the given info that _would_ be shown | 137 | * Builds a context menu specific to the given info that _would_ be shown |
106 | * immediately by {{showPopupMenu}}. Use this to add your own menu items to | 138 | * immediately by {{showPopupMenu}}. Use this to add your own menu items to |
107 | * the list but use most of the default behavior. | 139 | * the list but use most of the default behavior. |
108 | * | ||
109 | * @return {Promise<Menu>} The newly created `Menu` | ||
110 | */ | 140 | */ |
111 | async buildMenuForElement(info) { | 141 | async buildMenuForElement( |
142 | info: Electron.ContextMenuParams, | ||
143 | ): Promise<Electron.CrossProcessExports.Menu> { | ||
112 | if (info.linkURL && info.linkURL.length > 0) { | 144 | if (info.linkURL && info.linkURL.length > 0) { |
113 | return this.buildMenuForLink(info); | 145 | return this.buildMenuForLink(info); |
114 | } | 146 | } |
@@ -132,7 +164,9 @@ module.exports = class ContextMenuBuilder { | |||
132 | * | 164 | * |
133 | * @return {Menu} The `Menu` | 165 | * @return {Menu} The `Menu` |
134 | */ | 166 | */ |
135 | buildMenuForTextInput(menuInfo) { | 167 | buildMenuForTextInput( |
168 | menuInfo: Electron.ContextMenuParams, | ||
169 | ): Electron.CrossProcessExports.Menu { | ||
136 | const menu = new Menu(); | 170 | const menu = new Menu(); |
137 | 171 | ||
138 | this.addSpellingItems(menu, menuInfo); | 172 | this.addSpellingItems(menu, menuInfo); |
@@ -143,8 +177,10 @@ module.exports = class ContextMenuBuilder { | |||
143 | this.addPaste(menu, menuInfo); | 177 | this.addPaste(menu, menuInfo); |
144 | this.addPastePlain(menu, menuInfo); | 178 | this.addPastePlain(menu, menuInfo); |
145 | this.addInspectElement(menu, menuInfo); | 179 | this.addInspectElement(menu, menuInfo); |
180 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
146 | this.processMenu(menu, menuInfo); | 181 | this.processMenu(menu, menuInfo); |
147 | 182 | ||
183 | // @ts-expect-error Expected 2 arguments, but got 1. | ||
148 | this.copyPageUrl(menu); | 184 | this.copyPageUrl(menu); |
149 | this.goToHomePage(menu, menuInfo); | 185 | this.goToHomePage(menu, menuInfo); |
150 | this.openInBrowser(menu, menuInfo); | 186 | this.openInBrowser(menu, menuInfo); |
@@ -157,7 +193,9 @@ module.exports = class ContextMenuBuilder { | |||
157 | * | 193 | * |
158 | * @return {Menu} The `Menu` | 194 | * @return {Menu} The `Menu` |
159 | */ | 195 | */ |
160 | buildMenuForLink(menuInfo) { | 196 | buildMenuForLink( |
197 | menuInfo: Electron.ContextMenuParams, | ||
198 | ): Electron.CrossProcessExports.Menu { | ||
161 | const menu = new Menu(); | 199 | const menu = new Menu(); |
162 | const isEmailAddress = menuInfo.linkURL.startsWith('mailto:'); | 200 | const isEmailAddress = menuInfo.linkURL.startsWith('mailto:'); |
163 | 201 | ||
@@ -170,6 +208,7 @@ module.exports = class ContextMenuBuilder { | |||
170 | const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL; | 208 | const url = isEmailAddress ? menuInfo.linkText : menuInfo.linkURL; |
171 | clipboard.writeText(url); | 209 | clipboard.writeText(url); |
172 | this._sendNotificationOnClipboardEvent( | 210 | this._sendNotificationOnClipboardEvent( |
211 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
173 | menuInfo.clipboardNotifications, | 212 | menuInfo.clipboardNotifications, |
174 | () => `Link URL copied: ${url}`, | 213 | () => `Link URL copied: ${url}`, |
175 | ); | 214 | ); |
@@ -200,11 +239,13 @@ module.exports = class ContextMenuBuilder { | |||
200 | } | 239 | } |
201 | 240 | ||
202 | this.addInspectElement(menu, menuInfo); | 241 | this.addInspectElement(menu, menuInfo); |
242 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
203 | this.processMenu(menu, menuInfo); | 243 | this.processMenu(menu, menuInfo); |
204 | 244 | ||
205 | this.addSeparator(menu); | 245 | this.addSeparator(menu); |
206 | this.goBack(menu); | 246 | this.goBack(menu); |
207 | this.goForward(menu); | 247 | this.goForward(menu); |
248 | // @ts-expect-error Expected 2 arguments, but got 1. | ||
208 | this.copyPageUrl(menu); | 249 | this.copyPageUrl(menu); |
209 | this.goToHomePage(menu, menuInfo); | 250 | this.goToHomePage(menu, menuInfo); |
210 | this.openInBrowser(menu, menuInfo); | 251 | this.openInBrowser(menu, menuInfo); |
@@ -214,15 +255,16 @@ module.exports = class ContextMenuBuilder { | |||
214 | 255 | ||
215 | /** | 256 | /** |
216 | * Builds a menu applicable to a text field. | 257 | * Builds a menu applicable to a text field. |
217 | * | ||
218 | * @return {Menu} The `Menu` | ||
219 | */ | 258 | */ |
220 | buildMenuForText(menuInfo) { | 259 | buildMenuForText( |
260 | menuInfo: Electron.ContextMenuParams, | ||
261 | ): Electron.CrossProcessExports.Menu { | ||
221 | const menu = new Menu(); | 262 | const menu = new Menu(); |
222 | 263 | ||
223 | this.addSearchItems(menu, menuInfo); | 264 | this.addSearchItems(menu, menuInfo); |
224 | this.addCopy(menu, menuInfo); | 265 | this.addCopy(menu, menuInfo); |
225 | this.addInspectElement(menu, menuInfo); | 266 | this.addInspectElement(menu, menuInfo); |
267 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
226 | this.processMenu(menu, menuInfo); | 268 | this.processMenu(menu, menuInfo); |
227 | 269 | ||
228 | this.addSeparator(menu); | 270 | this.addSeparator(menu); |
@@ -240,13 +282,16 @@ module.exports = class ContextMenuBuilder { | |||
240 | * | 282 | * |
241 | * @return {Menu} The `Menu` | 283 | * @return {Menu} The `Menu` |
242 | */ | 284 | */ |
243 | buildMenuForImage(menuInfo) { | 285 | buildMenuForImage( |
286 | menuInfo: Electron.ContextMenuParams, | ||
287 | ): Electron.CrossProcessExports.Menu { | ||
244 | const menu = new Menu(); | 288 | const menu = new Menu(); |
245 | 289 | ||
246 | if (this.isSrcUrlValid(menuInfo)) { | 290 | if (this.isSrcUrlValid(menuInfo)) { |
247 | this.addImageItems(menu, menuInfo); | 291 | this.addImageItems(menu, menuInfo); |
248 | } | 292 | } |
249 | this.addInspectElement(menu, menuInfo); | 293 | this.addInspectElement(menu, menuInfo); |
294 | // @ts-expect-error Expected 1 arguments, but got 2. | ||
250 | this.processMenu(menu, menuInfo); | 295 | this.processMenu(menu, menuInfo); |
251 | 296 | ||
252 | return menu; | 297 | return menu; |
@@ -256,14 +301,17 @@ module.exports = class ContextMenuBuilder { | |||
256 | * Checks if the current text selection contains a single misspelled word and | 301 | * Checks if the current text selection contains a single misspelled word and |
257 | * if so, adds suggested spellings as individual menu items. | 302 | * if so, adds suggested spellings as individual menu items. |
258 | */ | 303 | */ |
259 | addSpellingItems(menu, menuInfo) { | 304 | addSpellingItems( |
305 | menu: Electron.CrossProcessExports.Menu, | ||
306 | menuInfo: Electron.ContextMenuParams, | ||
307 | ) { | ||
260 | const webContents = this.getWebContents(); | 308 | const webContents = this.getWebContents(); |
261 | // Add each spelling suggestion | 309 | // Add each spelling suggestion |
262 | for (const suggestion of menuInfo.dictionarySuggestions) { | 310 | for (const suggestion of menuInfo.dictionarySuggestions) { |
263 | menu.append( | 311 | menu.append( |
264 | new MenuItem({ | 312 | new MenuItem({ |
265 | label: suggestion, | 313 | label: suggestion, |
266 | // eslint-disable-next-line no-loop-func | 314 | |
267 | click: () => webContents.replaceMisspelling(suggestion), | 315 | click: () => webContents.replaceMisspelling(suggestion), |
268 | }), | 316 | }), |
269 | ); | 317 | ); |
@@ -288,7 +336,10 @@ module.exports = class ContextMenuBuilder { | |||
288 | /** | 336 | /** |
289 | * Adds search-related menu items. | 337 | * Adds search-related menu items. |
290 | */ | 338 | */ |
291 | addSearchItems(menu, menuInfo) { | 339 | addSearchItems( |
340 | menu: Electron.CrossProcessExports.Menu, | ||
341 | menuInfo: Electron.ContextMenuParams, | ||
342 | ) { | ||
292 | if (!menuInfo.selectionText || menuInfo.selectionText.length === 0) { | 343 | if (!menuInfo.selectionText || menuInfo.selectionText.length === 0) { |
293 | return menu; | 344 | return menu; |
294 | } | 345 | } |
@@ -313,9 +364,11 @@ module.exports = class ContextMenuBuilder { | |||
313 | 364 | ||
314 | const search = new MenuItem({ | 365 | const search = new MenuItem({ |
315 | label: this.stringTable.searchWith({ | 366 | label: this.stringTable.searchWith({ |
367 | // @ts-expect-error Property 'searchEngine' does not exist on type 'ContextMenuParams'. | ||
316 | searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine], | 368 | searchEngine: SEARCH_ENGINE_NAMES[menuInfo.searchEngine], |
317 | }), | 369 | }), |
318 | click: () => { | 370 | click: () => { |
371 | // @ts-expect-error Property 'searchEngine' does not exist on type 'ContextMenuParams'. | ||
319 | const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({ | 372 | const url = SEARCH_ENGINE_URLS[menuInfo.searchEngine]({ |
320 | searchTerm: encodeURIComponent(menuInfo.selectionText), | 373 | searchTerm: encodeURIComponent(menuInfo.selectionText), |
321 | }); | 374 | }); |
@@ -329,22 +382,28 @@ module.exports = class ContextMenuBuilder { | |||
329 | return menu; | 382 | return menu; |
330 | } | 383 | } |
331 | 384 | ||
332 | isSrcUrlValid(menuInfo) { | 385 | isSrcUrlValid(menuInfo: Electron.ContextMenuParams) { |
333 | return menuInfo.srcURL && menuInfo.srcURL.length > 0; | 386 | return menuInfo.srcURL && menuInfo.srcURL.length > 0; |
334 | } | 387 | } |
335 | 388 | ||
336 | /** | 389 | /** |
337 | * Adds "Copy Image" and "Copy Image URL" items when `src` is valid. | 390 | * Adds "Copy Image" and "Copy Image URL" items when `src` is valid. |
338 | */ | 391 | */ |
339 | addImageItems(menu, menuInfo) { | 392 | addImageItems( |
393 | menu: Electron.CrossProcessExports.Menu, | ||
394 | menuInfo: Electron.ContextMenuParams, | ||
395 | ) { | ||
340 | const copyImage = new MenuItem({ | 396 | const copyImage = new MenuItem({ |
341 | label: this.stringTable.copyImage(), | 397 | label: this.stringTable.copyImage(), |
342 | click: () => { | 398 | click: () => { |
343 | const result = this.convertImageToBase64(menuInfo.srcURL, dataURL => | 399 | const result = this.convertImageToBase64( |
344 | clipboard.writeImage(nativeImage.createFromDataURL(dataURL)), | 400 | menuInfo.srcURL, |
401 | (dataURL: string) => | ||
402 | clipboard.writeImage(nativeImage.createFromDataURL(dataURL)), | ||
345 | ); | 403 | ); |
346 | 404 | ||
347 | this._sendNotificationOnClipboardEvent( | 405 | this._sendNotificationOnClipboardEvent( |
406 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
348 | menuInfo.clipboardNotifications, | 407 | menuInfo.clipboardNotifications, |
349 | () => `Image copied from URL: ${menuInfo.srcURL}`, | 408 | () => `Image copied from URL: ${menuInfo.srcURL}`, |
350 | ); | 409 | ); |
@@ -359,6 +418,7 @@ module.exports = class ContextMenuBuilder { | |||
359 | click: () => { | 418 | click: () => { |
360 | const result = clipboard.writeText(menuInfo.srcURL); | 419 | const result = clipboard.writeText(menuInfo.srcURL); |
361 | this._sendNotificationOnClipboardEvent( | 420 | this._sendNotificationOnClipboardEvent( |
421 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
362 | menuInfo.clipboardNotifications, | 422 | menuInfo.clipboardNotifications, |
363 | () => `Image URL copied: ${menuInfo.srcURL}`, | 423 | () => `Image URL copied: ${menuInfo.srcURL}`, |
364 | ); | 424 | ); |
@@ -374,7 +434,7 @@ module.exports = class ContextMenuBuilder { | |||
374 | label: this.stringTable.downloadImage(), | 434 | label: this.stringTable.downloadImage(), |
375 | click: () => { | 435 | click: () => { |
376 | const urlWithoutBlob = menuInfo.srcURL.slice(5); | 436 | const urlWithoutBlob = menuInfo.srcURL.slice(5); |
377 | this.convertImageToBase64(menuInfo.srcURL, dataURL => { | 437 | this.convertImageToBase64(menuInfo.srcURL, (dataURL: any) => { |
378 | const url = new window.URL(urlWithoutBlob); | 438 | const url = new window.URL(urlWithoutBlob); |
379 | const fileName = url.pathname.slice(1); | 439 | const fileName = url.pathname.slice(1); |
380 | ipcRenderer.send('download-file', { | 440 | ipcRenderer.send('download-file', { |
@@ -386,6 +446,7 @@ module.exports = class ContextMenuBuilder { | |||
386 | }); | 446 | }); |
387 | }); | 447 | }); |
388 | this._sendNotificationOnClipboardEvent( | 448 | this._sendNotificationOnClipboardEvent( |
449 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. | ||
389 | menuInfo.clipboardNotifications, | 450 | menuInfo.clipboardNotifications, |
390 | () => `Image downloaded: ${urlWithoutBlob}`, | 451 | () => `Image downloaded: ${urlWithoutBlob}`, |
391 | ); | 452 | ); |
@@ -401,7 +462,10 @@ module.exports = class ContextMenuBuilder { | |||
401 | /** | 462 | /** |
402 | * Adds the Cut menu item | 463 | * Adds the Cut menu item |
403 | */ | 464 | */ |
404 | addCut(menu, menuInfo) { | 465 | addCut( |
466 | menu: Electron.CrossProcessExports.Menu, | ||
467 | menuInfo: Electron.ContextMenuParams, | ||
468 | ) { | ||
405 | const webContents = this.getWebContents(); | 469 | const webContents = this.getWebContents(); |
406 | menu.append( | 470 | menu.append( |
407 | new MenuItem({ | 471 | new MenuItem({ |
@@ -418,7 +482,10 @@ module.exports = class ContextMenuBuilder { | |||
418 | /** | 482 | /** |
419 | * Adds the Copy menu item. | 483 | * Adds the Copy menu item. |
420 | */ | 484 | */ |
421 | addCopy(menu, menuInfo) { | 485 | addCopy( |
486 | menu: Electron.CrossProcessExports.Menu, | ||
487 | menuInfo: Electron.ContextMenuParams, | ||
488 | ) { | ||
422 | const webContents = this.getWebContents(); | 489 | const webContents = this.getWebContents(); |
423 | menu.append( | 490 | menu.append( |
424 | new MenuItem({ | 491 | new MenuItem({ |
@@ -435,7 +502,10 @@ module.exports = class ContextMenuBuilder { | |||
435 | /** | 502 | /** |
436 | * Adds the Paste menu item. | 503 | * Adds the Paste menu item. |
437 | */ | 504 | */ |
438 | addPaste(menu, menuInfo) { | 505 | addPaste( |
506 | menu: Electron.CrossProcessExports.Menu, | ||
507 | menuInfo: Electron.ContextMenuParams, | ||
508 | ) { | ||
439 | const webContents = this.getWebContents(); | 509 | const webContents = this.getWebContents(); |
440 | menu.append( | 510 | menu.append( |
441 | new MenuItem({ | 511 | new MenuItem({ |
@@ -449,7 +519,10 @@ module.exports = class ContextMenuBuilder { | |||
449 | return menu; | 519 | return menu; |
450 | } | 520 | } |
451 | 521 | ||
452 | addPastePlain(menu, menuInfo) { | 522 | addPastePlain( |
523 | menu: Electron.CrossProcessExports.Menu, | ||
524 | menuInfo: Electron.ContextMenuParams, | ||
525 | ) { | ||
453 | if ( | 526 | if ( |
454 | menuInfo.editFlags.canPaste && | 527 | menuInfo.editFlags.canPaste && |
455 | !menuInfo.linkText && | 528 | !menuInfo.linkText && |
@@ -469,7 +542,7 @@ module.exports = class ContextMenuBuilder { | |||
469 | /** | 542 | /** |
470 | * Adds a separator item. | 543 | * Adds a separator item. |
471 | */ | 544 | */ |
472 | addSeparator(menu) { | 545 | addSeparator(menu: Electron.CrossProcessExports.Menu) { |
473 | menu.append(new MenuItem({ type: 'separator' })); | 546 | menu.append(new MenuItem({ type: 'separator' })); |
474 | return menu; | 547 | return menu; |
475 | } | 548 | } |
@@ -477,7 +550,11 @@ module.exports = class ContextMenuBuilder { | |||
477 | /** | 550 | /** |
478 | * Adds the "Inspect Element" menu item. | 551 | * Adds the "Inspect Element" menu item. |
479 | */ | 552 | */ |
480 | addInspectElement(menu, menuInfo, needsSeparator = true) { | 553 | addInspectElement( |
554 | menu: Electron.CrossProcessExports.Menu, | ||
555 | menuInfo: Electron.ContextMenuParams, | ||
556 | needsSeparator = true, | ||
557 | ) { | ||
481 | const webContents = this.getWebContents(); | 558 | const webContents = this.getWebContents(); |
482 | if (!this.debugMode) return menu; | 559 | if (!this.debugMode) return menu; |
483 | if (needsSeparator) this.addSeparator(menu); | 560 | if (needsSeparator) this.addSeparator(menu); |
@@ -498,21 +575,30 @@ module.exports = class ContextMenuBuilder { | |||
498 | * @param {Function} callback A callback that will be invoked with the result | 575 | * @param {Function} callback A callback that will be invoked with the result |
499 | * @param {String} outputFormat The image format to use, defaults to 'image/png' | 576 | * @param {String} outputFormat The image format to use, defaults to 'image/png' |
500 | */ | 577 | */ |
501 | convertImageToBase64(url, callback, outputFormat = 'image/png') { | 578 | convertImageToBase64( |
502 | let canvas = document.createElement('canvas'); | 579 | url: string, |
580 | callback: { | ||
581 | (dataURL: any): void; | ||
582 | (dataURL: any): void; | ||
583 | (arg0: string): void; | ||
584 | }, | ||
585 | outputFormat: string = 'image/png', | ||
586 | ) { | ||
587 | let canvas: HTMLCanvasElement | null = document.createElement('canvas'); | ||
503 | const ctx = canvas.getContext('2d'); | 588 | const ctx = canvas.getContext('2d'); |
504 | // eslint-disable-next-line no-undef | ||
505 | const img = new Image(); | 589 | const img = new Image(); |
506 | img.crossOrigin = 'Anonymous'; | 590 | img.crossOrigin = 'Anonymous'; |
507 | 591 | ||
508 | img.addEventListener('load', () => { | 592 | img.addEventListener('load', () => { |
509 | canvas.height = img.height; | 593 | if (canvas) { |
510 | canvas.width = img.width; | 594 | canvas.height = img.height; |
511 | ctx?.drawImage(img, 0, 0); | 595 | canvas.width = img.width; |
512 | 596 | ctx?.drawImage(img, 0, 0); | |
513 | const dataURL = canvas.toDataURL(outputFormat); | 597 | |
514 | canvas = null; | 598 | const dataURL = canvas.toDataURL(outputFormat); |
515 | callback(dataURL); | 599 | canvas = null; |
600 | callback(dataURL); | ||
601 | } | ||
516 | }); | 602 | }); |
517 | 603 | ||
518 | img.src = url; | 604 | img.src = url; |
@@ -521,7 +607,7 @@ module.exports = class ContextMenuBuilder { | |||
521 | /** | 607 | /** |
522 | * Adds the 'go back' menu item | 608 | * Adds the 'go back' menu item |
523 | */ | 609 | */ |
524 | goBack(menu) { | 610 | goBack(menu: Electron.CrossProcessExports.Menu) { |
525 | const webContents = this.getWebContents(); | 611 | const webContents = this.getWebContents(); |
526 | menu.append( | 612 | menu.append( |
527 | new MenuItem({ | 613 | new MenuItem({ |
@@ -538,7 +624,7 @@ module.exports = class ContextMenuBuilder { | |||
538 | /** | 624 | /** |
539 | * Adds the 'go forward' menu item | 625 | * Adds the 'go forward' menu item |
540 | */ | 626 | */ |
541 | goForward(menu) { | 627 | goForward(menu: Electron.CrossProcessExports.Menu) { |
542 | const webContents = this.getWebContents(); | 628 | const webContents = this.getWebContents(); |
543 | menu.append( | 629 | menu.append( |
544 | new MenuItem({ | 630 | new MenuItem({ |
@@ -555,7 +641,10 @@ module.exports = class ContextMenuBuilder { | |||
555 | /** | 641 | /** |
556 | * Adds the 'copy page url' menu item. | 642 | * Adds the 'copy page url' menu item. |
557 | */ | 643 | */ |
558 | copyPageUrl(menu, menuInfo) { | 644 | copyPageUrl( |
645 | menu: Electron.CrossProcessExports.Menu, | ||
646 | menuInfo: Electron.ContextMenuParams, | ||
647 | ) { | ||
559 | menu.append( | 648 | menu.append( |
560 | new MenuItem({ | 649 | new MenuItem({ |
561 | label: this.stringTable.copyPageUrl(), | 650 | label: this.stringTable.copyPageUrl(), |
@@ -563,7 +652,8 @@ module.exports = class ContextMenuBuilder { | |||
563 | click: () => { | 652 | click: () => { |
564 | clipboard.writeText(window.location.href); | 653 | clipboard.writeText(window.location.href); |
565 | this._sendNotificationOnClipboardEvent( | 654 | this._sendNotificationOnClipboardEvent( |
566 | menuInfo.clipboardNotifications, | 655 | // @ts-expect-error Property 'clipboardNotifications' does not exist on type 'ContextMenuParams'. |
656 | menuInfo?.clipboardNotifications, | ||
567 | () => `Page URL copied: ${window.location.href}`, | 657 | () => `Page URL copied: ${window.location.href}`, |
568 | ); | 658 | ); |
569 | }, | 659 | }, |
@@ -576,8 +666,11 @@ module.exports = class ContextMenuBuilder { | |||
576 | /** | 666 | /** |
577 | * Adds the 'go to home' menu item. | 667 | * Adds the 'go to home' menu item. |
578 | */ | 668 | */ |
579 | goToHomePage(menu, menuInfo) { | 669 | goToHomePage( |
580 | const baseURL = new URL(menuInfo.pageURL); | 670 | menu: Electron.CrossProcessExports.Menu, |
671 | menuInfo: Electron.ContextMenuParams, | ||
672 | ) { | ||
673 | const baseURL = new window.URL(menuInfo.pageURL); | ||
581 | menu.append( | 674 | menu.append( |
582 | new MenuItem({ | 675 | new MenuItem({ |
583 | label: this.stringTable.goToHomePage(), | 676 | label: this.stringTable.goToHomePage(), |
@@ -596,7 +689,10 @@ module.exports = class ContextMenuBuilder { | |||
596 | /** | 689 | /** |
597 | * Adds the 'open in browser' menu item. | 690 | * Adds the 'open in browser' menu item. |
598 | */ | 691 | */ |
599 | openInBrowser(menu, menuInfo) { | 692 | openInBrowser( |
693 | menu: Electron.CrossProcessExports.Menu, | ||
694 | menuInfo: Electron.ContextMenuParams, | ||
695 | ) { | ||
600 | menu.append( | 696 | menu.append( |
601 | new MenuItem({ | 697 | new MenuItem({ |
602 | label: this.stringTable.openInBrowser(), | 698 | label: this.stringTable.openInBrowser(), |
@@ -610,7 +706,10 @@ module.exports = class ContextMenuBuilder { | |||
610 | return menu; | 706 | return menu; |
611 | } | 707 | } |
612 | 708 | ||
613 | _sendNotificationOnClipboardEvent(isDisabled, notificationText) { | 709 | _sendNotificationOnClipboardEvent( |
710 | isDisabled: boolean, | ||
711 | notificationText: () => string, | ||
712 | ) { | ||
614 | if (isDisabled) { | 713 | if (isDisabled) { |
615 | return; | 714 | return; |
616 | } | 715 | } |
@@ -619,4 +718,4 @@ module.exports = class ContextMenuBuilder { | |||
619 | body: notificationText(), | 718 | body: notificationText(), |
620 | }); | 719 | }); |
621 | } | 720 | } |
622 | }; | 721 | } |
diff --git a/src/webview/darkmode/custom.js b/src/webview/darkmode/custom.ts index f767f5755..f767f5755 100644 --- a/src/webview/darkmode/custom.js +++ b/src/webview/darkmode/custom.ts | |||
diff --git a/src/webview/darkmode/ignore.js b/src/webview/darkmode/ignore.js deleted file mode 100644 index 110df364f..000000000 --- a/src/webview/darkmode/ignore.js +++ /dev/null | |||
@@ -1,3 +0,0 @@ | |||
1 | export default [ | ||
2 | 'discordapp.com', | ||
3 | ]; | ||
diff --git a/src/webview/darkmode/ignore.ts b/src/webview/darkmode/ignore.ts new file mode 100644 index 000000000..daa25d10c --- /dev/null +++ b/src/webview/darkmode/ignore.ts | |||
@@ -0,0 +1 @@ | |||
export default ['discordapp.com']; | |||
diff --git a/src/webview/find.js b/src/webview/find.ts index 040811d68..0665d9670 100644 --- a/src/webview/find.js +++ b/src/webview/find.ts | |||
@@ -3,11 +3,15 @@ import { FindInPage as ElectronFindInPage } from 'electron-find'; | |||
3 | 3 | ||
4 | // Shim to expose webContents functionality to electron-find without @electron/remote | 4 | // Shim to expose webContents functionality to electron-find without @electron/remote |
5 | const webContentsShim = { | 5 | const webContentsShim = { |
6 | findInPage: (text, options = {}) => ipcRenderer.sendSync('find-in-page', text, options), | 6 | findInPage: (text: string, options = {}) => |
7 | stopFindInPage: (action) => { | 7 | ipcRenderer.sendSync('find-in-page', text, options), |
8 | stopFindInPage: (action: any) => { | ||
8 | ipcRenderer.sendSync('stop-find-in-page', action); | 9 | ipcRenderer.sendSync('stop-find-in-page', action); |
9 | }, | 10 | }, |
10 | on: (eventName, listener) => { | 11 | on: ( |
12 | eventName: string, | ||
13 | listener: (arg0: { sender: undefined }, arg1: any) => void, | ||
14 | ): void => { | ||
11 | if (eventName === 'found-in-page') { | 15 | if (eventName === 'found-in-page') { |
12 | ipcRenderer.on('found-in-page', (_, result) => { | 16 | ipcRenderer.on('found-in-page', (_, result) => { |
13 | listener({ sender: this }, result); | 17 | listener({ sender: this }, result); |
diff --git a/src/webview/notifications.js b/src/webview/notifications.ts index 22960d818..73124b9a9 100644 --- a/src/webview/notifications.js +++ b/src/webview/notifications.ts | |||
@@ -5,9 +5,10 @@ import { v1 as uuidV1 } from 'uuid'; | |||
5 | const debug = require('debug')('Ferdi:Notifications'); | 5 | const debug = require('debug')('Ferdi:Notifications'); |
6 | 6 | ||
7 | export class NotificationsHandler { | 7 | export class NotificationsHandler { |
8 | onNotify = data => data; | 8 | onNotify = (data: { title: string; options: any; notificationId: string }) => |
9 | data; | ||
9 | 10 | ||
10 | displayNotification(title, options) { | 11 | displayNotification(title: string, options: any) { |
11 | return new Promise(resolve => { | 12 | return new Promise(resolve => { |
12 | debug('New notification', title, options); | 13 | debug('New notification', title, options); |
13 | 14 | ||
@@ -23,7 +24,7 @@ export class NotificationsHandler { | |||
23 | ); | 24 | ); |
24 | 25 | ||
25 | ipcRenderer.once(`notification-onclick:${notificationId}`, () => { | 26 | ipcRenderer.once(`notification-onclick:${notificationId}`, () => { |
26 | resolve(); | 27 | resolve(true); |
27 | }); | 28 | }); |
28 | }); | 29 | }); |
29 | } | 30 | } |
diff --git a/src/webview/screenshare.js b/src/webview/screenshare.ts index e7e43c04e..91a1623bb 100644 --- a/src/webview/screenshare.js +++ b/src/webview/screenshare.ts | |||
@@ -9,8 +9,8 @@ export async function getDisplayMediaSelector() { | |||
9 | return `<div class="desktop-capturer-selection__scroller"> | 9 | return `<div class="desktop-capturer-selection__scroller"> |
10 | <ul class="desktop-capturer-selection__list"> | 10 | <ul class="desktop-capturer-selection__list"> |
11 | ${sources | 11 | ${sources |
12 | .map( | 12 | .map( |
13 | ({ id, name, thumbnail }) => ` | 13 | ({ id, name, thumbnail }) => ` |
14 | <li class="desktop-capturer-selection__item"> | 14 | <li class="desktop-capturer-selection__item"> |
15 | <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> | 15 | <button class="desktop-capturer-selection__btn" data-id="${id}" title="${name}"> |
16 | <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> | 16 | <img class="desktop-capturer-selection__thumbnail" src="${thumbnail.toDataURL()}" /> |
@@ -18,8 +18,8 @@ export async function getDisplayMediaSelector() { | |||
18 | </button> | 18 | </button> |
19 | </li> | 19 | </li> |
20 | `, | 20 | `, |
21 | ) | 21 | ) |
22 | .join('')} | 22 | .join('')} |
23 | <li class="desktop-capturer-selection__item"> | 23 | <li class="desktop-capturer-selection__item"> |
24 | <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> | 24 | <button class="desktop-capturer-selection__btn" data-id="${CANCEL_ID}" title="Cancel"> |
25 | <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> | 25 | <span class="desktop-capturer-selection__name desktop-capturer-selection__name--cancel">Cancel</span> |
diff --git a/src/webview/spellchecker.ts b/src/webview/spellchecker.ts index 468a1b4ae..ee70589d5 100644 --- a/src/webview/spellchecker.ts +++ b/src/webview/spellchecker.ts | |||
@@ -5,7 +5,11 @@ import { isMac } from '../environment'; | |||
5 | const debug = require('debug')('Ferdi:spellchecker'); | 5 | const debug = require('debug')('Ferdi:spellchecker'); |
6 | 6 | ||
7 | export function getSpellcheckerLocaleByFuzzyIdentifier(identifier: string) { | 7 | export function getSpellcheckerLocaleByFuzzyIdentifier(identifier: string) { |
8 | const locales = Object.keys(SPELLCHECKER_LOCALES).filter((key) => key.toLocaleLowerCase() === identifier.toLowerCase() || key.split('-')[0] === identifier.toLowerCase()); | 8 | const locales = Object.keys(SPELLCHECKER_LOCALES).filter( |
9 | key => | ||
10 | key.toLocaleLowerCase() === identifier.toLowerCase() || | ||
11 | key.split('-')[0] === identifier.toLowerCase(), | ||
12 | ); | ||
9 | 13 | ||
10 | return locales.length > 0 ? locales[0] : null; | 14 | return locales.length > 0 ? locales[0] : null; |
11 | } | 15 | } |