aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--@types/index.d.ts11
-rw-r--r--package-lock.json12
-rw-r--r--package.json2
-rw-r--r--packages/forms/tsconfig.json2
-rw-r--r--packages/theme/tsconfig.json2
-rw-r--r--packages/ui/tsconfig.json2
m---------recipes0
-rw-r--r--src/actions/app.ts (renamed from src/actions/app.js)0
-rw-r--r--src/actions/index.ts (renamed from src/actions/index.js)0
-rw-r--r--src/actions/lib/actions.ts (renamed from src/actions/lib/actions.js)16
-rw-r--r--src/actions/news.ts (renamed from src/actions/news.js)0
-rw-r--r--src/actions/recipe.ts (renamed from src/actions/recipe.js)0
-rw-r--r--src/actions/recipePreview.ts (renamed from src/actions/recipePreview.js)0
-rw-r--r--src/actions/requests.ts (renamed from src/actions/requests.js)0
-rw-r--r--src/actions/service.ts (renamed from src/actions/service.js)0
-rw-r--r--src/actions/settings.ts (renamed from src/actions/settings.js)0
-rw-r--r--src/actions/ui.ts (renamed from src/actions/ui.js)0
-rw-r--r--src/actions/user.ts (renamed from src/actions/user.js)0
-rw-r--r--src/api/apiBase.ts (renamed from src/api/apiBase.js)28
-rw-r--r--src/components/ui/FeatureItem.js38
-rw-r--r--src/components/ui/FeatureList.js99
-rw-r--r--src/components/ui/Input.js10
-rw-r--r--src/features/appearance/index.js50
-rw-r--r--src/helpers/serverless-helpers.ts (renamed from src/helpers/serverless-helpers.js)0
-rw-r--r--src/helpers/service-helpers.ts (renamed from src/helpers/service-helpers.js)13
-rw-r--r--src/helpers/validation-helpers.ts (renamed from src/helpers/validation-helpers.js)20
-rw-r--r--src/i18n/locales/en-US.json11
-rw-r--r--src/index.js61
-rw-r--r--src/stores/UIStore.ts (renamed from src/stores/UIStore.js)45
-rw-r--r--tsconfig.json31
-rw-r--r--tsconfig.settings.json28
-rw-r--r--uidev/tsconfig.json18
32 files changed, 203 insertions, 296 deletions
diff --git a/@types/index.d.ts b/@types/index.d.ts
new file mode 100644
index 000000000..e21f88cee
--- /dev/null
+++ b/@types/index.d.ts
@@ -0,0 +1,11 @@
1declare global {
2 interface Window {
3 ferdi: any;
4 }
5}
6
7/**
8 * Workaround to make TS recognize this file as a module.
9 * https://fettblog.eu/typescript-augmenting-global-lib-dom/
10 */
11export {};
diff --git a/package-lock.json b/package-lock.json
index 9d988e332..2128f78e3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -6613,12 +6613,6 @@
6613 "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", 6613 "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
6614 "dev": true 6614 "dev": true
6615 }, 6615 },
6616 "@tsconfig/node14": {
6617 "version": "1.0.1",
6618 "resolved": "https://registry.npmjs.org/@tsconfig/node14/-/node14-1.0.1.tgz",
6619 "integrity": "sha512-509r2+yARFfHHE7T6Puu2jjkoycftovhXRqW328PDXTVGKihlb1P8Z9mMZH04ebyajfRY7dedfGynlrFHJUQCg==",
6620 "dev": true
6621 },
6622 "@types/babel__core": { 6616 "@types/babel__core": {
6623 "version": "7.1.15", 6617 "version": "7.1.15",
6624 "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz", 6618 "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.15.tgz",
@@ -6975,6 +6969,12 @@
6975 "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==", 6969 "integrity": "sha512-XDwyIlt/47l2kWLTzw/mtrpLdB+GPSskR2n/PIcPn+VYhVO77rGhRncIR5GPU0KRzXuqkDO+J5qqrG0Y8P6jzQ==",
6976 "dev": true 6970 "dev": true
6977 }, 6971 },
6972 "@types/validator": {
6973 "version": "13.6.3",
6974 "resolved": "https://registry.npmjs.org/@types/validator/-/validator-13.6.3.tgz",
6975 "integrity": "sha512-fWG42pMJOL4jKsDDZZREnXLjc3UE0R8LOJfARWYg6U966rxDT7TYejYzLnUF5cvSObGg34nd0+H2wHHU5Omdfw==",
6976 "dev": true
6977 },
6978 "@types/verror": { 6978 "@types/verror": {
6979 "version": "1.10.5", 6979 "version": "1.10.5",
6980 "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz", 6980 "resolved": "https://registry.npmjs.org/@types/verror/-/verror-1.10.5.tgz",
diff --git a/package.json b/package.json
index 638dca627..9b9382e3c 100644
--- a/package.json
+++ b/package.json
@@ -149,7 +149,6 @@
149 "@commitlint/cli": "13.1.0", 149 "@commitlint/cli": "13.1.0",
150 "@commitlint/config-conventional": "13.1.0", 150 "@commitlint/config-conventional": "13.1.0",
151 "@formatjs/cli": "4.2.33", 151 "@formatjs/cli": "4.2.33",
152 "@tsconfig/node14": "1.0.1",
153 "@types/color": "3.0.2", 152 "@types/color": "3.0.2",
154 "@types/du": "1.0.1", 153 "@types/du": "1.0.1",
155 "@types/expect.js": "0.3.29", 154 "@types/expect.js": "0.3.29",
@@ -163,6 +162,7 @@
163 "@types/route-parser": "0.1.3", 162 "@types/route-parser": "0.1.3",
164 "@types/tar": "4.0.5", 163 "@types/tar": "4.0.5",
165 "@types/uuid": "3.4.9", 164 "@types/uuid": "3.4.9",
165 "@types/validator": "13.6.3",
166 "@typescript-eslint/eslint-plugin": "4.30.0", 166 "@typescript-eslint/eslint-plugin": "4.30.0",
167 "@typescript-eslint/parser": "4.29.1", 167 "@typescript-eslint/parser": "4.29.1",
168 "all-contributors-cli": "6.20.0", 168 "all-contributors-cli": "6.20.0",
diff --git a/packages/forms/tsconfig.json b/packages/forms/tsconfig.json
index 8b9507eac..015581136 100644
--- a/packages/forms/tsconfig.json
+++ b/packages/forms/tsconfig.json
@@ -1,5 +1,5 @@
1{ 1{
2 "extends": "../../tsconfig.settings.json", 2 "extends": "../../tsconfig.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "outDir": "lib", 4 "outDir": "lib",
5 "rootDir": "src" 5 "rootDir": "src"
diff --git a/packages/theme/tsconfig.json b/packages/theme/tsconfig.json
index 4deaa5dd7..8b4163e7f 100644
--- a/packages/theme/tsconfig.json
+++ b/packages/theme/tsconfig.json
@@ -1,5 +1,5 @@
1{ 1{
2 "extends": "../../tsconfig.settings.json", 2 "extends": "../../tsconfig.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "outDir": "lib", 4 "outDir": "lib",
5 "rootDir": "src" 5 "rootDir": "src"
diff --git a/packages/ui/tsconfig.json b/packages/ui/tsconfig.json
index 8b9507eac..015581136 100644
--- a/packages/ui/tsconfig.json
+++ b/packages/ui/tsconfig.json
@@ -1,5 +1,5 @@
1{ 1{
2 "extends": "../../tsconfig.settings.json", 2 "extends": "../../tsconfig.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "outDir": "lib", 4 "outDir": "lib",
5 "rootDir": "src" 5 "rootDir": "src"
diff --git a/recipes b/recipes
Subproject 9db43e100a672b6d6932ac68c0fbe503c129138 Subproject 2c5236b5251c7224a346f2a00a9d7e487aca3e6
diff --git a/src/actions/app.js b/src/actions/app.ts
index e6f7f22ba..e6f7f22ba 100644
--- a/src/actions/app.js
+++ b/src/actions/app.ts
diff --git a/src/actions/index.js b/src/actions/index.ts
index aecdac675..aecdac675 100644
--- a/src/actions/index.js
+++ b/src/actions/index.ts
diff --git a/src/actions/lib/actions.js b/src/actions/lib/actions.ts
index 7be40f1cf..ed42eabc0 100644
--- a/src/actions/lib/actions.js
+++ b/src/actions/lib/actions.ts
@@ -1,6 +1,6 @@
1export const createActionsFromDefinitions = (actionDefinitions, validate) => { 1export const createActionsFromDefinitions = (actionDefinitions, validate) => {
2 const actions = {}; 2 const actions = {};
3 Object.keys(actionDefinitions).forEach((actionName) => { 3 Object.keys(actionDefinitions).forEach(actionName => {
4 const action = (params = {}) => { 4 const action = (params = {}) => {
5 const schema = actionDefinitions[actionName]; 5 const schema = actionDefinitions[actionName];
6 validate(schema, params, actionName); 6 validate(schema, params, actionName);
@@ -8,20 +8,24 @@ export const createActionsFromDefinitions = (actionDefinitions, validate) => {
8 }; 8 };
9 actions[actionName] = action; 9 actions[actionName] = action;
10 action.listeners = []; 10 action.listeners = [];
11 action.listen = (listener) => action.listeners.push(listener); 11 action.listen = listener => action.listeners.push(listener);
12 action.off = (listener) => { 12 action.off = listener => {
13 const { listeners } = action; 13 const { listeners } = action;
14 listeners.splice(listeners.indexOf(listener), 1); 14 listeners.splice(listeners.indexOf(listener), 1);
15 }; 15 };
16 action.notify = (params) => action.listeners.forEach((listener) => listener(params)); 16 action.notify = params =>
17 action.listeners.forEach(listener => listener(params));
17 }); 18 });
18 return actions; 19 return actions;
19}; 20};
20 21
21export default (definitions, validate) => { 22export default (definitions, validate) => {
22 const newActions = {}; 23 const newActions = {};
23 Object.keys(definitions).forEach((scopeName) => { 24 Object.keys(definitions).forEach(scopeName => {
24 newActions[scopeName] = createActionsFromDefinitions(definitions[scopeName], validate); 25 newActions[scopeName] = createActionsFromDefinitions(
26 definitions[scopeName],
27 validate,
28 );
25 }); 29 });
26 return newActions; 30 return newActions;
27}; 31};
diff --git a/src/actions/news.js b/src/actions/news.ts
index db106e84f..db106e84f 100644
--- a/src/actions/news.js
+++ b/src/actions/news.ts
diff --git a/src/actions/recipe.js b/src/actions/recipe.ts
index 29b0a151f..29b0a151f 100644
--- a/src/actions/recipe.js
+++ b/src/actions/recipe.ts
diff --git a/src/actions/recipePreview.js b/src/actions/recipePreview.ts
index 36de3d844..36de3d844 100644
--- a/src/actions/recipePreview.js
+++ b/src/actions/recipePreview.ts
diff --git a/src/actions/requests.js b/src/actions/requests.ts
index 89296e7ec..89296e7ec 100644
--- a/src/actions/requests.js
+++ b/src/actions/requests.ts
diff --git a/src/actions/service.js b/src/actions/service.ts
index e56513f8f..e56513f8f 100644
--- a/src/actions/service.js
+++ b/src/actions/service.ts
diff --git a/src/actions/settings.js b/src/actions/settings.ts
index fd29b798b..fd29b798b 100644
--- a/src/actions/settings.js
+++ b/src/actions/settings.ts
diff --git a/src/actions/ui.js b/src/actions/ui.ts
index b913b430b..b913b430b 100644
--- a/src/actions/ui.js
+++ b/src/actions/ui.ts
diff --git a/src/actions/user.js b/src/actions/user.ts
index 20d27ee53..20d27ee53 100644
--- a/src/actions/user.js
+++ b/src/actions/user.ts
diff --git a/src/api/apiBase.js b/src/api/apiBase.ts
index 2fad7eb21..dc10fad91 100644
--- a/src/api/apiBase.js
+++ b/src/api/apiBase.ts
@@ -1,9 +1,7 @@
1/** 1/**
2 * Get API base URL from store 2 * Get API base URL from store
3 */ 3 */
4import { 4import { API_VERSION } from '../environment';
5 API_VERSION,
6} from '../environment';
7import { 5import {
8 DEV_API_FRANZ_WEBSITE, 6 DEV_API_FRANZ_WEBSITE,
9 LIVE_FRANZ_API, 7 LIVE_FRANZ_API,
@@ -14,21 +12,25 @@ import {
14 12
15// Note: This cannot be used from the internal-server since we are not running within the context of a browser window 13// Note: This cannot be used from the internal-server since we are not running within the context of a browser window
16const apiBase = (withVersion = true) => { 14const apiBase = (withVersion = true) => {
17 let url; 15 let url: string;
18 16
19 if (!window.ferdi 17 if (
20 || !window.ferdi.stores.settings 18 !(window as any).ferdi ||
21 || !window.ferdi.stores.settings.all 19 !(window as any).ferdi.stores.settings ||
22 || !window.ferdi.stores.settings.all.app.server) { 20 !(window as any).ferdi.stores.settings.all ||
21 !(window as any).ferdi.stores.settings.all.app.server
22 ) {
23 // Stores have not yet been loaded - return SERVER_NOT_LOADED to force a retry when stores are loaded 23 // Stores have not yet been loaded - return SERVER_NOT_LOADED to force a retry when stores are loaded
24 return SERVER_NOT_LOADED; 24 return SERVER_NOT_LOADED;
25 } 25 }
26 if (window.ferdi.stores.settings.all.app.server === LOCAL_SERVER) { 26 if ((window as any).ferdi.stores.settings.all.app.server === LOCAL_SERVER) {
27 // Use URL for local server 27 // Use URL for local server
28 url = `http://${LOCAL_HOSTNAME}:${window.ferdi.stores.requests.localServerPort}`; 28 url = `http://${LOCAL_HOSTNAME}:${
29 (window as any).ferdi.stores.requests.localServerPort
30 }`;
29 } else { 31 } else {
30 // Load URL from store 32 // Load URL from store
31 url = window.ferdi.stores.settings.all.app.server; 33 url = (window as any).ferdi.stores.settings.all.app.server;
32 } 34 }
33 35
34 return withVersion ? `${url}/${API_VERSION}` : url; 36 return withVersion ? `${url}/${API_VERSION}` : url;
@@ -38,5 +40,7 @@ export default apiBase;
38 40
39export function termsBase() { 41export function termsBase() {
40 // TODO: This needs to handle local vs ferdi vs franz servers 42 // TODO: This needs to handle local vs ferdi vs franz servers
41 return window.ferdi.stores.settings.all.app.server !== LIVE_FRANZ_API ? window.ferdi.stores.settings.all.app.server : DEV_API_FRANZ_WEBSITE; 43 return (window as any).ferdi.stores.settings.all.app.server !== LIVE_FRANZ_API
44 ? (window as any).ferdi.stores.settings.all.app.server
45 : DEV_API_FRANZ_WEBSITE;
42} 46}
diff --git a/src/components/ui/FeatureItem.js b/src/components/ui/FeatureItem.js
deleted file mode 100644
index 646cf56ca..000000000
--- a/src/components/ui/FeatureItem.js
+++ /dev/null
@@ -1,38 +0,0 @@
1import React from 'react';
2import injectSheet from 'react-jss';
3import { Icon } from '@meetfranz/ui';
4import classnames from 'classnames';
5import { mdiCheckCircle } from '@mdi/js';
6
7const styles = (theme) => ({
8 featureItem: {
9 borderBottom: [1, 'solid', theme.defaultContentBorder],
10 padding: [8, 0],
11 display: 'flex',
12 alignItems: 'center',
13 textAlign: 'left',
14 },
15 featureIcon: {
16 fill: theme.brandSuccess,
17 marginRight: 10,
18 },
19});
20
21export const FeatureItem = injectSheet(styles)(({
22 classes, className, name, icon,
23}) => (
24 <li className={classnames({
25 [classes.featureItem]: true,
26 [className]: className,
27 })}
28 >
29 {icon ? (
30 <span className={classes.featureIcon}>{icon}</span>
31 ) : (
32 <Icon icon={mdiCheckCircle} className={classes.featureIcon} size={1.5} />
33 )}
34 {name}
35 </li>
36));
37
38export default FeatureItem;
diff --git a/src/components/ui/FeatureList.js b/src/components/ui/FeatureList.js
deleted file mode 100644
index 14e7ec3c4..000000000
--- a/src/components/ui/FeatureList.js
+++ /dev/null
@@ -1,99 +0,0 @@
1import React, { Component } from 'react';
2import PropTypes from 'prop-types';
3import { defineMessages, injectIntl } from 'react-intl';
4
5import { FeatureItem } from './FeatureItem';
6
7const messages = defineMessages({
8 availableRecipes: {
9 id: 'pricing.features.recipes',
10 defaultMessage: 'Choose from more than 70 Services', // TODO: Make this dynamic
11 },
12 accountSync: {
13 id: 'pricing.features.accountSync',
14 defaultMessage: 'Account Synchronisation',
15 },
16 desktopNotifications: {
17 id: 'pricing.features.desktopNotifications',
18 defaultMessage: 'Desktop Notifications',
19 },
20 unlimitedServices: {
21 id: 'pricing.features.unlimitedServices',
22 defaultMessage: 'Add unlimited services',
23 },
24 spellchecker: {
25 id: 'pricing.features.spellchecker',
26 defaultMessage: 'Spellchecker support',
27 },
28 workspaces: {
29 id: 'pricing.features.workspaces',
30 defaultMessage: 'Workspaces',
31 },
32 customWebsites: {
33 id: 'pricing.features.customWebsites',
34 defaultMessage: 'Add Custom Websites',
35 },
36 onPremise: {
37 id: 'pricing.features.onPremise',
38 defaultMessage: 'On-premise & other Hosted Services',
39 },
40 thirdPartyServices: {
41 id: 'pricing.features.thirdPartyServices',
42 defaultMessage: 'Install 3rd party services',
43 },
44 serviceProxies: {
45 id: 'pricing.features.serviceProxies',
46 defaultMessage: 'Service Proxies',
47 },
48 teamManagement: {
49 id: 'pricing.features.teamManagement',
50 defaultMessage: 'Team Management',
51 },
52});
53
54export class FeatureList extends Component {
55 static propTypes = {
56 className: PropTypes.string,
57 featureClassName: PropTypes.string,
58 };
59
60 static defaultProps = {
61 className: '',
62 featureClassName: '',
63 };
64
65 render() {
66 const { className, featureClassName } = this.props;
67 const { intl } = this.props;
68
69 const features = [
70 messages.availableRecipes,
71 messages.accountSync,
72 messages.desktopNotifications,
73
74 messages.spellchecker,
75
76 messages.workspaces,
77 messages.customWebsites,
78 messages.thirdPartyServices,
79
80 messages.unlimitedServices,
81 messages.onPremise,
82 messages.serviceProxies,
83 messages.teamManagement,
84 ];
85
86 return (
87 <ul className={className}>
88 {features.map(feature => (
89 <FeatureItem
90 name={intl.formatMessage(feature)}
91 className={featureClassName}
92 />
93 ))}
94 </ul>
95 );
96 }
97}
98
99export default injectIntl(FeatureList);
diff --git a/src/components/ui/Input.js b/src/components/ui/Input.js
index 335367f03..43fab10ee 100644
--- a/src/components/ui/Input.js
+++ b/src/components/ui/Input.js
@@ -42,7 +42,7 @@ class Input extends Component {
42 passwordScore: 0, 42 passwordScore: 0,
43 }; 43 };
44 44
45 inputElement = null; 45 inputElement;
46 46
47 componentDidMount() { 47 componentDidMount() {
48 if (this.props.focus) { 48 if (this.props.focus) {
@@ -133,10 +133,10 @@ class Input extends Component {
133 {/* <progress value={this.state.passwordScore} max="100" /> */} 133 {/* <progress value={this.state.passwordScore} max="100" /> */}
134 <meter 134 <meter
135 value={passwordScore < 5 ? 5 : passwordScore} 135 value={passwordScore < 5 ? 5 : passwordScore}
136 low="30" 136 low={30}
137 high="75" 137 high={75}
138 optimum="100" 138 optimum={100}
139 max="100" 139 max={100}
140 /> 140 />
141 </div> 141 </div>
142 )} 142 )}
diff --git a/src/features/appearance/index.js b/src/features/appearance/index.js
index 3aab2fcad..d1db68ac6 100644
--- a/src/features/appearance/index.js
+++ b/src/features/appearance/index.js
@@ -10,13 +10,15 @@ function createStyleElement() {
10 const styles = document.createElement('style'); 10 const styles = document.createElement('style');
11 styles.id = STYLE_ELEMENT_ID; 11 styles.id = STYLE_ELEMENT_ID;
12 12
13 document.querySelector('head').appendChild(styles); 13 document.querySelector('head')?.appendChild(styles);
14} 14}
15 15
16function setAppearance(style) { 16function setAppearance(style) {
17 const styleElement = document.getElementById(STYLE_ELEMENT_ID); 17 const styleElement = document.getElementById(STYLE_ELEMENT_ID);
18 18
19 styleElement.innerHTML = style; 19 if (styleElement) {
20 styleElement.innerHTML = style;
21 }
20} 22}
21 23
22// See https://github.com/Qix-/color/issues/53#issuecomment-656590710 24// See https://github.com/Qix-/color/issues/53#issuecomment-656590710
@@ -28,7 +30,7 @@ function darkenAbsolute(originalColor, absoluteChange) {
28function generateAccentStyle(accentColorStr) { 30function generateAccentStyle(accentColorStr) {
29 let style = ''; 31 let style = '';
30 32
31 Object.keys(themeInfo).forEach((property) => { 33 Object.keys(themeInfo).forEach(property => {
32 style += ` 34 style += `
33 ${themeInfo[property]} { 35 ${themeInfo[property]} {
34 ${property}: ${accentColorStr}; 36 ${property}: ${accentColorStr};
@@ -80,19 +82,21 @@ function generateServiceRibbonWidthStyle(widthStr, iconSizeStr, vertical) {
80 const width = Number(widthStr); 82 const width = Number(widthStr);
81 const iconSize = Number(iconSizeStr) - iconSizeBias; 83 const iconSize = Number(iconSizeStr) - iconSizeBias;
82 84
83 return vertical ? ` 85 return vertical
86 ? `
84 .tab-item { 87 .tab-item {
85 width: ${width - 2}px !important; 88 width: ${width - 2}px !important;
86 height: ${width - 5 + iconSize}px !important; 89 height: ${width - 5 + iconSize}px !important;
87 min-height: unset; 90 min-height: unset;
88 } 91 }
89 .tab-item .tab-item__icon { 92 .tab-item .tab-item__icon {
90 width: ${(width / 2) + iconSize}px !important; 93 width: ${width / 2 + iconSize}px !important;
91 } 94 }
92 .sidebar__button { 95 .sidebar__button {
93 font-size: ${width / 3}px !important; 96 font-size: ${width / 3}px !important;
94 } 97 }
95 ` : ` 98 `
99 : `
96 .sidebar { 100 .sidebar {
97 width: ${width}px !important; 101 width: ${width}px !important;
98 } 102 }
@@ -101,7 +105,7 @@ function generateServiceRibbonWidthStyle(widthStr, iconSizeStr, vertical) {
101 height: ${width - 5 + iconSize}px !important; 105 height: ${width - 5 + iconSize}px !important;
102 } 106 }
103 .tab-item .tab-item__icon { 107 .tab-item .tab-item__icon {
104 width: ${(width / 2) + iconSize}px !important; 108 width: ${width / 2 + iconSize}px !important;
105 } 109 }
106 .sidebar__button { 110 .sidebar__button {
107 font-size: ${width / 3}px !important; 111 font-size: ${width / 3}px !important;
@@ -145,9 +149,13 @@ function generateVerticalStyle(widthStr, alwaysShowWorkspaces) {
145 return ` 149 return `
146 .sidebar { 150 .sidebar {
147 height: ${sidebarWidth + verticalStyleOffset + 1}px !important; 151 height: ${sidebarWidth + verticalStyleOffset + 1}px !important;
148 ${alwaysShowWorkspaces ? ` 152 ${
153 alwaysShowWorkspaces
154 ? `
149 width: calc(100% - 300px) !important; 155 width: calc(100% - 300px) !important;
150 ` : ''} 156 `
157 : ''
158}
151 } 159 }
152 160
153 .sidebar .sidebar__button { 161 .sidebar .sidebar__button {
@@ -192,12 +200,20 @@ function generateStyle(settings) {
192 alwaysShowWorkspaces, 200 alwaysShowWorkspaces,
193 } = settings; 201 } = settings;
194 202
195 if (accentColor.toLowerCase() !== DEFAULT_APP_SETTINGS.accentColor.toLowerCase()) { 203 if (
204 accentColor.toLowerCase() !== DEFAULT_APP_SETTINGS.accentColor.toLowerCase()
205 ) {
196 style += generateAccentStyle(accentColor); 206 style += generateAccentStyle(accentColor);
197 } 207 }
198 if (serviceRibbonWidth !== DEFAULT_APP_SETTINGS.serviceRibbonWidth 208 if (
199 || iconSize !== DEFAULT_APP_SETTINGS.iconSize) { 209 serviceRibbonWidth !== DEFAULT_APP_SETTINGS.serviceRibbonWidth ||
200 style += generateServiceRibbonWidthStyle(serviceRibbonWidth, iconSize, useVerticalStyle); 210 iconSize !== DEFAULT_APP_SETTINGS.iconSize
211 ) {
212 style += generateServiceRibbonWidthStyle(
213 serviceRibbonWidth,
214 iconSize,
215 useVerticalStyle,
216 );
201 } 217 }
202 if (showDragArea) { 218 if (showDragArea) {
203 style += generateShowDragAreaStyle(accentColor); 219 style += generateShowDragAreaStyle(accentColor);
@@ -206,7 +222,9 @@ function generateStyle(settings) {
206 style += generateVerticalStyle(serviceRibbonWidth, alwaysShowWorkspaces); 222 style += generateVerticalStyle(serviceRibbonWidth, alwaysShowWorkspaces);
207 } else if (document.getElementById('vertical-style')) { 223 } else if (document.getElementById('vertical-style')) {
208 const link = document.getElementById('vertical-style'); 224 const link = document.getElementById('vertical-style');
209 document.head.removeChild(link); 225 if (link) {
226 document.head.removeChild(link);
227 }
210 } 228 }
211 if (alwaysShowWorkspaces) { 229 if (alwaysShowWorkspaces) {
212 style += generateOpenWorkspaceStyle(); 230 style += generateOpenWorkspaceStyle();
@@ -225,14 +243,14 @@ export default function initAppearance(stores) {
225 243
226 // Update style when settings change 244 // Update style when settings change
227 reaction( 245 reaction(
228 () => ([ 246 () => [
229 settings.all.app.accentColor, 247 settings.all.app.accentColor,
230 settings.all.app.serviceRibbonWidth, 248 settings.all.app.serviceRibbonWidth,
231 settings.all.app.iconSize, 249 settings.all.app.iconSize,
232 settings.all.app.showDragArea, 250 settings.all.app.showDragArea,
233 settings.all.app.useVerticalStyle, 251 settings.all.app.useVerticalStyle,
234 settings.all.app.alwaysShowWorkspaces, 252 settings.all.app.alwaysShowWorkspaces,
235 ]), 253 ],
236 () => { 254 () => {
237 updateStyle(settings.all.app); 255 updateStyle(settings.all.app);
238 }, 256 },
diff --git a/src/helpers/serverless-helpers.js b/src/helpers/serverless-helpers.ts
index 01549e038..01549e038 100644
--- a/src/helpers/serverless-helpers.js
+++ b/src/helpers/serverless-helpers.ts
diff --git a/src/helpers/service-helpers.js b/src/helpers/service-helpers.ts
index 745f40dd9..13c921f88 100644
--- a/src/helpers/service-helpers.js
+++ b/src/helpers/service-helpers.ts
@@ -2,15 +2,20 @@ import { readdirSync, removeSync } from 'fs-extra';
2import { userDataPath } from '../environment'; 2import { userDataPath } from '../environment';
3 3
4export function getServicePartitionsDirectory(...segments) { 4export function getServicePartitionsDirectory(...segments) {
5 return userDataPath('Partitions', ...([segments].flat())); 5 return userDataPath('Partitions', ...[segments].flat());
6} 6}
7 7
8export function removeServicePartitionDirectory(id = '', addServicePrefix = false) { 8export function removeServicePartitionDirectory(
9 const servicePartition = getServicePartitionsDirectory(`${addServicePrefix ? 'service-' : ''}${id}`); 9 id = '',
10 addServicePrefix = false,
11) {
12 const servicePartition = getServicePartitionsDirectory(
13 `${addServicePrefix ? 'service-' : ''}${id}`,
14 );
10 return removeSync(servicePartition); 15 return removeSync(servicePartition);
11} 16}
12 17
13export async function getServiceIdsFromPartitions() { 18export async function getServiceIdsFromPartitions() {
14 const files = readdirSync(getServicePartitionsDirectory()); 19 const files = readdirSync(getServicePartitionsDirectory());
15 return files.filter((n) => n !== '__chrome_extension'); 20 return files.filter(n => n !== '__chrome_extension');
16} 21}
diff --git a/src/helpers/validation-helpers.js b/src/helpers/validation-helpers.ts
index 569b13bb0..80d368b2e 100644
--- a/src/helpers/validation-helpers.js
+++ b/src/helpers/validation-helpers.ts
@@ -28,7 +28,9 @@ export function required({ field }) {
28 const isValid = field.value.trim() !== ''; 28 const isValid = field.value.trim() !== '';
29 return [ 29 return [
30 isValid, 30 isValid,
31 window.ferdi.intl.formatMessage(messages.required, { field: field.label }), 31 (window as any).ferdi.intl.formatMessage(messages.required, {
32 field: field.label,
33 }),
32 ]; 34 ];
33} 35}
34 36
@@ -37,7 +39,9 @@ export function email({ field }) {
37 const isValid = isEmail(value); 39 const isValid = isEmail(value);
38 return [ 40 return [
39 isValid, 41 isValid,
40 window.ferdi.intl.formatMessage(messages.email, { field: field.label }), 42 (window as any).ferdi.intl.formatMessage(messages.email, {
43 field: field.label,
44 }),
41 ]; 45 ];
42} 46}
43 47
@@ -58,11 +62,13 @@ export function url({ field }) {
58 62
59 return [ 63 return [
60 isValid, 64 isValid,
61 window.ferdi.intl.formatMessage(messages.url, { field: field.label }), 65 (window as any).ferdi.intl.formatMessage(messages.url, {
66 field: field.label,
67 }),
62 ]; 68 ];
63} 69}
64 70
65export function minLength(length) { 71export function minLength(length: number) {
66 return ({ field }) => { 72 return ({ field }) => {
67 let isValid = true; 73 let isValid = true;
68 if (field.touched) { 74 if (field.touched) {
@@ -70,7 +76,7 @@ export function minLength(length) {
70 } 76 }
71 return [ 77 return [
72 isValid, 78 isValid,
73 window.ferdi.intl.formatMessage(messages.minLength, { 79 (window as any).ferdi.intl.formatMessage(messages.minLength, {
74 field: field.label, 80 field: field.label,
75 length, 81 length,
76 }), 82 }),
@@ -78,12 +84,12 @@ export function minLength(length) {
78 }; 84 };
79} 85}
80 86
81export function oneRequired(targets) { 87export function oneRequired(targets: string[]) {
82 return ({ field, form }) => { 88 return ({ field, form }) => {
83 const invalidFields = targets.filter(target => form.$(target).value === ''); 89 const invalidFields = targets.filter(target => form.$(target).value === '');
84 return [ 90 return [
85 targets.length !== invalidFields.length, 91 targets.length !== invalidFields.length,
86 window.ferdi.intl.formatMessage(messages.required, { 92 (window as any).ferdi.intl.formatMessage(messages.required, {
87 field: field.label, 93 field: field.label,
88 }), 94 }),
89 ]; 95 ];
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 19adaaf6a..c66056467 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -152,17 +152,6 @@
152 "password.link.signup": "Create a free account", 152 "password.link.signup": "Create a free account",
153 "password.noUser": "No user affiliated with that email address", 153 "password.noUser": "No user affiliated with that email address",
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 "pricing.features.accountSync": "Account Synchronisation",
156 "pricing.features.customWebsites": "Add Custom Websites",
157 "pricing.features.desktopNotifications": "Desktop Notifications",
158 "pricing.features.onPremise": "On-premise & other Hosted Services",
159 "pricing.features.recipes": "Choose from more than 70 Services",
160 "pricing.features.serviceProxies": "Service Proxies",
161 "pricing.features.spellchecker": "Spellchecker support",
162 "pricing.features.teamManagement": "Team Management",
163 "pricing.features.thirdPartyServices": "Install 3rd party services",
164 "pricing.features.unlimitedServices": "Add unlimited services",
165 "pricing.features.workspaces": "Workspaces",
166 "service.crashHandler.action": "Reload {name}", 155 "service.crashHandler.action": "Reload {name}",
167 "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds", 156 "service.crashHandler.autoReload": "Trying to automatically restore {name} in {seconds} seconds",
168 "service.crashHandler.headline": "Oh no!", 157 "service.crashHandler.headline": "Oh no!",
diff --git a/src/index.js b/src/index.js
index 7f1f77b4e..08d9a3a45 100644
--- a/src/index.js
+++ b/src/index.js
@@ -78,14 +78,18 @@ if (isWindows) {
78const settings = new Settings('app', DEFAULT_APP_SETTINGS); 78const settings = new Settings('app', DEFAULT_APP_SETTINGS);
79const proxySettings = new Settings('proxy'); 79const proxySettings = new Settings('proxy');
80 80
81const retrieveSettingValue = (key, defaultValue = true) => ifUndefinedBoolean(settings.get(key), defaultValue); 81const retrieveSettingValue = (key, defaultValue = true) =>
82 ifUndefinedBoolean(settings.get(key), defaultValue);
82 83
83if (retrieveSettingValue('sentry')) { 84if (retrieveSettingValue('sentry')) {
84 // eslint-disable-next-line global-require 85 // eslint-disable-next-line global-require
85 require('./sentry'); 86 require('./sentry');
86} 87}
87 88
88const liftSingleInstanceLock = retrieveSettingValue('liftSingleInstanceLock', false); 89const liftSingleInstanceLock = retrieveSettingValue(
90 'liftSingleInstanceLock',
91 false,
92);
89 93
90// Force single window 94// Force single window
91const gotTheLock = liftSingleInstanceLock 95const gotTheLock = liftSingleInstanceLock
@@ -190,11 +194,11 @@ const createWindow = () => {
190 minWidth: 600, 194 minWidth: 600,
191 minHeight: 500, 195 minHeight: 500,
192 show: false, 196 show: false,
193 titleBarStyle: isMac ? 'hidden' : '', 197 titleBarStyle: isMac ? 'hidden' : 'default',
194 frame: isLinux, 198 frame: isLinux,
195 spellcheck: retrieveSettingValue('enableSpellchecking'),
196 backgroundColor, 199 backgroundColor,
197 webPreferences: { 200 webPreferences: {
201 spellcheck: retrieveSettingValue('enableSpellchecking'),
198 nodeIntegration: true, 202 nodeIntegration: true,
199 contextIsolation: false, 203 contextIsolation: false,
200 webviewTag: true, 204 webviewTag: true,
@@ -268,10 +272,7 @@ const createWindow = () => {
268 // Dereference the window object, usually you would store windows 272 // Dereference the window object, usually you would store windows
269 // in an array if your app supports multi windows, this is the time 273 // in an array if your app supports multi windows, this is the time
270 // when you should delete the corresponding element. 274 // when you should delete the corresponding element.
271 if ( 275 if (!willQuitApp && retrieveSettingValue('runInBackground')) {
272 !willQuitApp &&
273 retrieveSettingValue('runInBackground')
274 ) {
275 e.preventDefault(); 276 e.preventDefault();
276 if (isWindows) { 277 if (isWindows) {
277 debug('Window: minimize'); 278 debug('Window: minimize');
@@ -471,7 +472,9 @@ ipcMain.on('open-browser-window', (e, { url, serviceId }) => {
471ipcMain.on( 472ipcMain.on(
472 'modifyRequestHeaders', 473 'modifyRequestHeaders',
473 (e, { modifiedRequestHeaders, serviceId }) => { 474 (e, { modifiedRequestHeaders, serviceId }) => {
474 debug(`Received modifyRequestHeaders ${modifiedRequestHeaders} for serviceId ${serviceId}`); 475 debug(
476 `Received modifyRequestHeaders ${modifiedRequestHeaders} for serviceId ${serviceId}`,
477 );
475 modifiedRequestHeaders.forEach(headerFilterSet => { 478 modifiedRequestHeaders.forEach(headerFilterSet => {
476 const { headers, requestFilters } = headerFilterSet; 479 const { headers, requestFilters } = headerFilterSet;
477 session 480 session
@@ -489,23 +492,22 @@ ipcMain.on(
489 }, 492 },
490); 493);
491 494
492ipcMain.on( 495ipcMain.on('knownCertificateHosts', (e, { knownHosts, serviceId }) => {
493 'knownCertificateHosts', 496 debug(
494 (e, { knownHosts, serviceId }) => { 497 `Received knownCertificateHosts ${knownHosts} for serviceId ${serviceId}`,
495 debug(`Received knownCertificateHosts ${knownHosts} for serviceId ${serviceId}`); 498 );
496 session 499 session
497 .fromPartition(`persist:service-${serviceId}`) 500 .fromPartition(`persist:service-${serviceId}`)
498 .setCertificateVerifyProc((request, callback) => { 501 .setCertificateVerifyProc((request, callback) => {
499 // To know more about these callbacks: https://www.electronjs.org/docs/api/session#sessetcertificateverifyprocproc 502 // To know more about these callbacks: https://www.electronjs.org/docs/api/session#sessetcertificateverifyprocproc
500 const { hostname } = request; 503 const { hostname } = request;
501 if (knownHosts.find(item => item.includes(hostname)).length > 0) { 504 if (knownHosts.find(item => item.includes(hostname)).length > 0) {
502 callback(0); 505 callback(0);
503 } else { 506 } else {
504 callback(-2); 507 callback(-2);
505 } 508 }
506 }); 509 });
507 }, 510});
508);
509 511
510ipcMain.on('feature-basic-auth-cancel', () => { 512ipcMain.on('feature-basic-auth-cancel', () => {
511 debug('Cancel basic auth'); 513 debug('Cancel basic auth');
@@ -560,7 +562,7 @@ app.on('window-all-closed', () => {
560 } 562 }
561}); 563});
562 564
563app.on('before-quit', (event) => { 565app.on('before-quit', event => {
564 const yesButtonIndex = 0; 566 const yesButtonIndex = 0;
565 let selection = yesButtonIndex; 567 let selection = yesButtonIndex;
566 if (retrieveSettingValue('confirmOnQuit')) { 568 if (retrieveSettingValue('confirmOnQuit')) {
@@ -568,10 +570,7 @@ app.on('before-quit', (event) => {
568 type: 'question', 570 type: 'question',
569 message: 'Quit', 571 message: 'Quit',
570 detail: 'Do you really want to quit Ferdi?', 572 detail: 'Do you really want to quit Ferdi?',
571 buttons: [ 573 buttons: ['Yes', 'No'],
572 'Yes',
573 'No',
574 ],
575 }); 574 });
576 } 575 }
577 if (selection === yesButtonIndex) { 576 if (selection === yesButtonIndex) {
diff --git a/src/stores/UIStore.js b/src/stores/UIStore.ts
index be675d5ed..ec48eeb40 100644
--- a/src/stores/UIStore.js
+++ b/src/stores/UIStore.ts
@@ -1,13 +1,15 @@
1import { 1import { action, observable, computed, reaction } from 'mobx';
2 action, observable, computed, reaction, 2import { theme, ThemeType } from '@meetfranz/theme';
3} from 'mobx';
4import { theme } from '@meetfranz/theme';
5import { nativeTheme, systemPreferences } from '@electron/remote'; 3import { nativeTheme, systemPreferences } from '@electron/remote';
6 4
7import Store from './lib/Store'; 5import Store from './lib/Store';
8import { isMac, isWindows } from '../environment'; 6import { isMac, isWindows } from '../environment';
9 7
10export default class UIStore extends Store { 8export default class UIStore extends Store {
9 actions: any;
10
11 stores: any;
12
11 @observable showServicesUpdatedInfoBar = false; 13 @observable showServicesUpdatedInfoBar = false;
12 14
13 @observable isOsDarkThemeActive = nativeTheme.shouldUseDarkColors; 15 @observable isOsDarkThemeActive = nativeTheme.shouldUseDarkColors;
@@ -43,9 +45,7 @@ export default class UIStore extends Store {
43 45
44 setup() { 46 setup() {
45 reaction( 47 reaction(
46 () => ( 48 () => this.isDarkThemeActive,
47 this.isDarkThemeActive
48 ),
49 () => { 49 () => {
50 this._setupThemeInDOM(); 50 this._setupThemeInDOM();
51 }, 51 },
@@ -57,24 +57,31 @@ export default class UIStore extends Store {
57 const settings = this.stores.settings.all; 57 const settings = this.stores.settings.all;
58 58
59 return ( 59 return (
60 (settings.app.isAppMuted && settings.app.showMessageBadgeWhenMuted) 60 (settings.app.isAppMuted && settings.app.showMessageBadgeWhenMuted) ||
61 || !settings.app.isAppMuted 61 !settings.app.isAppMuted
62 ); 62 );
63 } 63 }
64 64
65 @computed get isDarkThemeActive() { 65 @computed get isDarkThemeActive() {
66 const isWithAdaptableInDarkMode = this.stores.settings.all.app.adaptableDarkMode 66 const isWithAdaptableInDarkMode =
67 && this.isOsDarkThemeActive; 67 this.stores.settings.all.app.adaptableDarkMode &&
68 const isWithoutAdaptableInDarkMode = this.stores.settings.all.app.darkMode 68 this.isOsDarkThemeActive;
69 && !this.stores.settings.all.app.adaptableDarkMode; 69 const isWithoutAdaptableInDarkMode =
70 this.stores.settings.all.app.darkMode &&
71 !this.stores.settings.all.app.adaptableDarkMode;
70 const isInDarkMode = this.stores.settings.all.app.darkMode; 72 const isInDarkMode = this.stores.settings.all.app.darkMode;
71 return !!(isWithAdaptableInDarkMode 73 return !!(
72 || isWithoutAdaptableInDarkMode 74 isWithAdaptableInDarkMode ||
73 || isInDarkMode); 75 isWithoutAdaptableInDarkMode ||
76 isInDarkMode
77 );
74 } 78 }
75 79
76 @computed get theme() { 80 @computed get theme() {
77 const themeId = (this.isDarkThemeActive || this.stores.settings.app.darkMode) ? 'dark' : 'default'; 81 const themeId =
82 this.isDarkThemeActive || this.stores.settings.app.darkMode
83 ? ThemeType.dark
84 : ThemeType.default;
78 const { accentColor } = this.stores.settings.app; 85 const { accentColor } = this.stores.settings.app;
79 return theme(themeId, accentColor); 86 return theme(themeId, accentColor);
80 } 87 }
@@ -102,9 +109,9 @@ export default class UIStore extends Store {
102 const body = document.querySelector('body'); 109 const body = document.querySelector('body');
103 110
104 if (!this.isDarkThemeActive) { 111 if (!this.isDarkThemeActive) {
105 body.classList.remove('theme__dark'); 112 body?.classList.remove('theme__dark');
106 } else { 113 } else {
107 body.classList.add('theme__dark'); 114 body?.classList.add('theme__dark');
108 } 115 }
109 } 116 }
110} 117}
diff --git a/tsconfig.json b/tsconfig.json
index 2676f392d..c01c40f68 100644
--- a/tsconfig.json
+++ b/tsconfig.json
@@ -1,7 +1,34 @@
1{ 1{
2 "extends": "./tsconfig.settings.json",
3 "compilerOptions": { 2 "compilerOptions": {
4 "outDir": ".tmp", 3 "outDir": ".tmp",
5 "rootDir": "./" 4 "rootDir": "./",
5 "baseUrl": ".",
6 "strict": true,
7 "target": "esnext",
8 "lib": ["esnext", "dom"],
9 "module": "commonjs",
10 "jsx": "react",
11 "typeRoots": ["@types", "node_modules/@types"],
12 "moduleResolution": "node",
13 "types": ["node"],
14 "sourceMap": true,
15 "skipLibCheck": true,
16 "noImplicitAny": false, // TODO: Need to switch
17 "allowSyntheticDefaultImports": true,
18 "experimentalDecorators": true,
19 "composite": true,
20 "esModuleInterop": true,
21 "importHelpers": true,
22 "removeComments": true,
23 "allowUnreachableCode": false,
24 "allowUnusedLabels": false,
25 "noUnusedLocals": true,
26 "noUnusedParameters": true,
27 "noImplicitReturns": true,
28 "noImplicitThis": true,
29 "preserveConstEnums": true,
30 "strictNullChecks": true,
31 "resolveJsonModule": true,
32 "forceConsistentCasingInFileNames": true
6 } 33 }
7} 34}
diff --git a/tsconfig.settings.json b/tsconfig.settings.json
deleted file mode 100644
index bd4669e01..000000000
--- a/tsconfig.settings.json
+++ /dev/null
@@ -1,28 +0,0 @@
1{
2 "extends": "@tsconfig/node14/tsconfig.json",
3 "compilerOptions": {
4 "baseUrl": ".",
5 "target": "esnext",
6 "lib": ["es2015", "es2017", "dom"],
7 "jsx": "react",
8 "typeRoots": ["node_modules/@types"],
9 "moduleResolution": "node",
10 "types": ["node"],
11 "sourceMap": true,
12 "noImplicitAny": false, // TODO: Need to switch
13 "allowSyntheticDefaultImports": true,
14 "experimentalDecorators": true,
15 "composite": true,
16 "esModuleInterop": true,
17 "importHelpers": true,
18 "removeComments": true,
19 "allowUnreachableCode": false,
20 "allowUnusedLabels": false,
21 "noUnusedLocals": true,
22 "noUnusedParameters": true,
23 "noImplicitReturns": true,
24 "noImplicitThis": true,
25 "preserveConstEnums": true,
26 "strictNullChecks": true,
27 }
28}
diff --git a/uidev/tsconfig.json b/uidev/tsconfig.json
index fb57639c8..60459d69e 100644
--- a/uidev/tsconfig.json
+++ b/uidev/tsconfig.json
@@ -1,14 +1,16 @@
1{ 1{
2 "extends": "../tsconfig.settings.json", 2 "extends": "../tsconfig.json",
3 "compilerOptions": { 3 "compilerOptions": {
4 "baseUrl": "..", 4 "baseUrl": "..",
5 "outDir": "lib", 5 "outDir": "lib",
6 "rootDir": "src", 6 "rootDir": "src"
7 }, 7 },
8 "references": [{ 8 "references": [
9 "path": "../packages/theme" 9 {
10 }, 10 "path": "../packages/theme"
11 { 11 },
12 "path": "../packages/forms" 12 {
13 }] 13 "path": "../packages/forms"
14 }
15 ]
14} 16}