aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/@types/legacy-types.ts5
-rw-r--r--src/components/auth/Import.js168
-rw-r--r--src/components/auth/SetupAssistant.tsx (renamed from src/components/auth/SetupAssistant.jsx)65
-rw-r--r--src/components/services/content/ServiceWebview.tsx (renamed from src/components/services/content/ServiceWebview.jsx)48
-rw-r--r--src/components/services/content/Services.tsx (renamed from src/components/services/content/Services.jsx)81
-rw-r--r--src/components/settings/SettingsLayout.tsx (renamed from src/components/settings/SettingsLayout.jsx)47
-rw-r--r--src/components/settings/settings/EditSettingsForm.tsx25
-rw-r--r--src/components/ui/ColorPickerInput.tsx102
-rw-r--r--src/components/ui/colorPickerInput/index.tsx88
-rw-r--r--src/components/ui/effects/Appear.tsx21
-rw-r--r--src/containers/auth/ImportScreen.tsx25
-rw-r--r--src/containers/auth/SetupAssistantScreen.tsx133
-rw-r--r--src/features/workspaces/actions.ts1
-rw-r--r--src/features/workspaces/components/WorkspaceDrawer.tsx (renamed from src/features/workspaces/components/WorkspaceDrawer.jsx)32
-rw-r--r--src/i18n/locales/en-US.json4
-rw-r--r--src/models/Recipe.ts1
-rw-r--r--src/routes.tsx5
17 files changed, 333 insertions, 518 deletions
diff --git a/src/@types/legacy-types.ts b/src/@types/legacy-types.ts
new file mode 100644
index 000000000..c17fdfe82
--- /dev/null
+++ b/src/@types/legacy-types.ts
@@ -0,0 +1,5 @@
1export interface ILegacyServices {
2 [key: string]: {
3 [key: string]: string | boolean;
4 };
5}
diff --git a/src/components/auth/Import.js b/src/components/auth/Import.js
deleted file mode 100644
index b897116e2..000000000
--- a/src/components/auth/Import.js
+++ /dev/null
@@ -1,168 +0,0 @@
1import { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react';
4import { defineMessages, injectIntl } from 'react-intl';
5import { Link } from 'react-router-dom';
6import classnames from 'classnames';
7import Form from '../../lib/Form';
8import Toggle from '../ui/toggle';
9import Button from '../ui/button';
10import { H1 } from '../ui/headline';
11
12const messages = defineMessages({
13 headline: {
14 id: 'import.headline',
15 defaultMessage: 'Import your Ferdium 4 services',
16 },
17 notSupportedHeadline: {
18 id: 'import.notSupportedHeadline',
19 defaultMessage: 'Services not yet supported in Ferdium 5',
20 },
21 submitButtonLabel: {
22 id: 'import.submit.label',
23 defaultMessage: 'Import {count} services',
24 },
25 skipButtonLabel: {
26 id: 'import.skip.label',
27 defaultMessage: 'I want to add services manually',
28 },
29});
30
31class Import extends Component {
32 static propTypes = {
33 services: MobxPropTypes.arrayOrObservableArray.isRequired,
34 onSubmit: PropTypes.func.isRequired,
35 isSubmitting: PropTypes.bool.isRequired,
36 inviteRoute: PropTypes.string.isRequired,
37 };
38
39 componentDidMount() {
40 const config = {
41 fields: {
42 import: [
43 ...this.props.services
44 .filter(s => s.recipe)
45 .map(s => ({
46 fields: {
47 add: {
48 default: true,
49 options: s,
50 },
51 },
52 })),
53 ],
54 },
55 };
56
57 this.form = new Form(config, this.props.intl);
58 }
59
60 submit(e) {
61 const { services } = this.props;
62 e.preventDefault();
63 this.form.submit({
64 onSuccess: form => {
65 const servicesImport = form
66 .values()
67 .import.map(
68 (value, i) => !value.add || services.filter(s => s.recipe)[i],
69 )
70 .filter(s => typeof s !== 'boolean');
71
72 this.props.onSubmit({ services: servicesImport });
73 },
74 onError: () => {},
75 });
76 }
77
78 render() {
79 const { intl } = this.props;
80 const { services, isSubmitting, inviteRoute } = this.props;
81
82 const availableServices = services.filter(s => s.recipe);
83 const unavailableServices = services.filter(s => !s.recipe);
84
85 return (
86 <div className="auth__scroll-container">
87 <div className="auth__container auth__container--signup">
88 <form
89 className="franz-form auth__form"
90 onSubmit={e => this.submit(e)}
91 >
92 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
93 <H1>{intl.formatMessage(messages.headline)}</H1>
94 <table className="service-table available-services">
95 <tbody>
96 {this.form.$('import').map((service, i) => (
97 <tr key={service.id} className="service-table__row">
98 <td className="service-table__toggle">
99 <Toggle {...service.$('add').bind()} showLabel={false} />
100 </td>
101 <td className="service-table__column-icon">
102 <img
103 src={
104 availableServices[i].custom_icon ||
105 availableServices[i].recipe.icons.svg
106 }
107 className={classnames({
108 'service-table__icon': true,
109 'has-custom-icon': availableServices[i].custom_icon,
110 })}
111 alt=""
112 />
113 </td>
114 <td className="service-table__column-name">
115 {availableServices[i].name !== ''
116 ? availableServices[i].name
117 : availableServices[i].recipe.name}
118 </td>
119 </tr>
120 ))}
121 </tbody>
122 </table>
123 {unavailableServices.length > 0 && (
124 <div className="unavailable-services">
125 <strong>
126 {intl.formatMessage(messages.notSupportedHeadline)}
127 </strong>
128 <p>
129 {services
130 .filter(s => !s.recipe)
131 .map((service, i) => (
132 <span key={service.id}>
133 {service.name !== '' ? service.name : service.service}
134 {unavailableServices.length > i + 1 ? ', ' : ''}
135 </span>
136 ))}
137 </p>
138 </div>
139 )}
140
141 {isSubmitting ? (
142 <Button
143 className="auth__button is-loading"
144 label={`${intl.formatMessage(messages.submitButtonLabel)} ...`}
145 loaded={false}
146 disabled
147 />
148 ) : (
149 <Button
150 type="submit"
151 className="auth__button"
152 label={intl.formatMessage(messages.submitButtonLabel)}
153 />
154 )}
155 <Link
156 to={inviteRoute}
157 className="franz-form__button franz-form__button--secondary auth__button auth__button--skip"
158 >
159 {intl.formatMessage(messages.skipButtonLabel)}
160 </Link>
161 </form>
162 </div>
163 </div>
164 );
165 }
166}
167
168export default injectIntl(observer(Import));
diff --git a/src/components/auth/SetupAssistant.jsx b/src/components/auth/SetupAssistant.tsx
index 8d15e36d1..c5fb79919 100644
--- a/src/components/auth/SetupAssistant.jsx
+++ b/src/components/auth/SetupAssistant.tsx
@@ -1,10 +1,8 @@
1import { Component } from 'react'; 1import { Component } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 2import { observer } from 'mobx-react';
4import { defineMessages, injectIntl } from 'react-intl'; 3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
5import injectSheet from 'react-jss'; 4import withStyles, { WithStylesProps } from 'react-jss';
6import classnames from 'classnames'; 5import classnames from 'classnames';
7
8import Input from '../ui/input/index'; 6import Input from '../ui/input/index';
9import Button from '../ui/button'; 7import Button from '../ui/button';
10import Badge from '../ui/badge'; 8import Badge from '../ui/badge';
@@ -12,7 +10,6 @@ import Modal from '../ui/Modal';
12import Infobox from '../ui/Infobox'; 10import Infobox from '../ui/Infobox';
13import Appear from '../ui/effects/Appear'; 11import Appear from '../ui/effects/Appear';
14import globalMessages from '../../i18n/globalMessages'; 12import globalMessages from '../../i18n/globalMessages';
15
16import { CDN_URL } from '../../config'; 13import { CDN_URL } from '../../config';
17import { H1, H2 } from '../ui/headline'; 14import { H1, H2 } from '../ui/headline';
18 15
@@ -42,11 +39,10 @@ const messages = defineMessages({
42 }, 39 },
43}); 40});
44 41
45let transition = 'none'; 42const transition =
46 43 window && window.matchMedia('(prefers-reduced-motion: no-preference)')
47if (window && window.matchMedia('(prefers-reduced-motion: no-preference)')) { 44 ? 'all 0.25s'
48 transition = 'all 0.25s'; 45 : 'none';
49}
50 46
51const styles = theme => ({ 47const styles = theme => ({
52 root: { 48 root: {
@@ -136,39 +132,41 @@ const styles = theme => ({
136 }, 132 },
137}); 133});
138 134
139class SetupAssistant extends Component { 135interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps {
140 static propTypes = { 136 onSubmit: (...args: any[]) => void;
141 classes: PropTypes.object.isRequired, 137 isInviteSuccessful?: boolean;
142 onSubmit: PropTypes.func.isRequired, 138 services: any; // TODO - [TS DEBT] check legacy services type
143 isInviteSuccessful: PropTypes.bool, 139 isSettingUpServices: boolean;
144 services: PropTypes.object.isRequired, 140}
145 isSettingUpServices: PropTypes.bool.isRequired,
146 };
147 141
148 static defaultProps = { 142interface IState {
149 isInviteSuccessful: false, 143 services: any[];
150 }; 144 isSlackModalOpen: boolean;
145 slackWorkspace: string;
146 showSuccessInfo: boolean;
147}
151 148
152 constructor() { 149@observer
153 super(); 150class SetupAssistant extends Component<IProps, IState> {
151 constructor(props: IProps) {
152 super(props);
154 153
155 this.state = { 154 this.state = {
156 services: [], 155 services: [],
157 isSlackModalOpen: false, 156 isSlackModalOpen: false,
157 showSuccessInfo: false,
158 slackWorkspace: '', 158 slackWorkspace: '',
159 }; 159 };
160 } 160 }
161 161
162 slackWorkspaceHandler() { 162 slackWorkspaceHandler(): void {
163 const { slackWorkspace = '', services } = this.state; 163 const { slackWorkspace = '', services } = this.state;
164
165 const sanitizedWorkspace = slackWorkspace 164 const sanitizedWorkspace = slackWorkspace
166 .trim() 165 .trim()
167 .replace(/^https?:\/\//, ''); 166 .replace(/^https?:\/\//, '');
168 167
169 if (sanitizedWorkspace) { 168 if (sanitizedWorkspace) {
170 const index = services.findIndex(s => s.id === SLACK_ID); 169 const index = services.findIndex(s => s.id === SLACK_ID);
171
172 if (index === -1) { 170 if (index === -1) {
173 const newServices = services; 171 const newServices = services;
174 newServices.push({ id: SLACK_ID, team: sanitizedWorkspace }); 172 newServices.push({ id: SLACK_ID, team: sanitizedWorkspace });
@@ -183,13 +181,13 @@ class SetupAssistant extends Component {
183 } 181 }
184 182
185 render() { 183 render() {
186 const { intl } = this.props;
187 const { 184 const {
188 classes, 185 classes,
189 isInviteSuccessful, 186 isInviteSuccessful = false,
190 onSubmit, 187 onSubmit,
191 services, 188 services,
192 isSettingUpServices, 189 isSettingUpServices,
190 intl,
193 } = this.props; 191 } = this.props;
194 const { 192 const {
195 isSlackModalOpen, 193 isSlackModalOpen,
@@ -204,7 +202,7 @@ class SetupAssistant extends Component {
204 <Infobox 202 <Infobox
205 type="success" 203 type="success"
206 icon="checkbox-marked-circle-outline" 204 icon="checkbox-marked-circle-outline"
207 dismissable 205 dismissible
208 > 206 >
209 {intl.formatMessage(messages.inviteSuccessInfo)} 207 {intl.formatMessage(messages.inviteSuccessInfo)}
210 </Infobox> 208 </Infobox>
@@ -300,11 +298,14 @@ class SetupAssistant extends Component {
300 label={intl.formatMessage(globalMessages.save)} 298 label={intl.formatMessage(globalMessages.save)}
301 /> 299 />
302 <Button 300 <Button
303 type="link" 301 type="button"
304 buttonType="secondary" 302 buttonType="secondary"
305 label={intl.formatMessage(globalMessages.cancel)} 303 label={intl.formatMessage(globalMessages.cancel)}
306 className={classes.ctaCancel} 304 className={classes.ctaCancel}
307 onClick={() => this.setState({ slackWorkspace: '' })} 305 onClick={e => {
306 e.preventDefault();
307 this.setState({ slackWorkspace: '' });
308 }}
308 /> 309 />
309 </div> 310 </div>
310 </form> 311 </form>
@@ -332,5 +333,5 @@ class SetupAssistant extends Component {
332} 333}
333 334
334export default injectIntl( 335export default injectIntl(
335 injectSheet(styles, { injectTheme: true })(observer(SetupAssistant)), 336 withStyles(styles, { injectTheme: true })(SetupAssistant),
336); 337);
diff --git a/src/components/services/content/ServiceWebview.jsx b/src/components/services/content/ServiceWebview.tsx
index 835c5125e..ac8d1ea66 100644
--- a/src/components/services/content/ServiceWebview.jsx
+++ b/src/components/services/content/ServiceWebview.tsx
@@ -1,27 +1,32 @@
1import { Component } from 'react'; 1import { Component, ReactElement } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 2import { observer } from 'mobx-react';
4import { action, makeObservable, observable, reaction } from 'mobx'; 3import { action, makeObservable, observable, reaction } from 'mobx';
5import ElectronWebView from 'react-electron-web-view'; 4import ElectronWebView from 'react-electron-web-view';
6import { join } from 'path'; 5import { join } from 'path';
7
8import ServiceModel from '../../../models/Service'; 6import ServiceModel from '../../../models/Service';
9 7
10const debug = require('../../../preload-safe-debug')('Ferdium:Services'); 8const debug = require('../../../preload-safe-debug')('Ferdium:Services');
11 9
12class ServiceWebview extends Component { 10interface IProps {
13 static propTypes = { 11 service: ServiceModel;
14 service: PropTypes.instanceOf(ServiceModel).isRequired, 12 setWebviewReference: (options: {
15 setWebviewReference: PropTypes.func.isRequired, 13 serviceId: string;
16 detachService: PropTypes.func.isRequired, 14 webview: ElectronWebView | null;
17 isSpellcheckerEnabled: PropTypes.bool.isRequired, 15 }) => void;
18 }; 16 detachService: (options: { service: ServiceModel }) => void;
17 isSpellcheckerEnabled: boolean;
18}
19 19
20 @observable webview = null; 20@observer
21class ServiceWebview extends Component<IProps> {
22 @observable webview: ElectronWebView | null = null;
21 23
22 constructor(props) { 24 constructor(props: IProps) {
23 super(props); 25 super(props);
24 26
27 this.refocusWebview = this.refocusWebview.bind(this);
28 this._setWebview = this._setWebview.bind(this);
29
25 makeObservable(this); 30 makeObservable(this);
26 31
27 reaction( 32 reaction(
@@ -45,15 +50,18 @@ class ServiceWebview extends Component {
45 ); 50 );
46 } 51 }
47 52
48 componentWillUnmount() { 53 componentWillUnmount(): void {
49 const { service, detachService } = this.props; 54 const { service, detachService } = this.props;
50 detachService({ service }); 55 detachService({ service });
51 } 56 }
52 57
53 refocusWebview = () => { 58 refocusWebview(): void {
54 const { webview } = this; 59 const { webview } = this;
55 debug('Refocus Webview is called', this.props.service); 60 debug('Refocus Webview is called', this.props.service);
56 if (!webview) return; 61 if (!webview) {
62 return;
63 }
64
57 if (this.props.service.isActive) { 65 if (this.props.service.isActive) {
58 webview.view.blur(); 66 webview.view.blur();
59 webview.view.focus(); 67 webview.view.focus();
@@ -67,13 +75,13 @@ class ServiceWebview extends Component {
67 } else { 75 } else {
68 debug('Refocus not required - Not active service'); 76 debug('Refocus not required - Not active service');
69 } 77 }
70 }; 78 }
71 79
72 @action _setWebview(webview) { 80 @action _setWebview(webview): void {
73 this.webview = webview; 81 this.webview = webview;
74 } 82 }
75 83
76 render() { 84 render(): ReactElement {
77 const { service, setWebviewReference, isSpellcheckerEnabled } = this.props; 85 const { service, setWebviewReference, isSpellcheckerEnabled } = this.props;
78 86
79 const preloadScript = join( 87 const preloadScript = join(
@@ -114,7 +122,7 @@ class ServiceWebview extends Component {
114 }); 122 });
115 }, 0); 123 }, 0);
116 }} 124 }}
117 onUpdateTargetUrl={this.updateTargetUrl} 125 // onUpdateTargetUrl={this.updateTargetUrl} // TODO - [TS DEBT] need to check where its from
118 useragent={service.userAgent} 126 useragent={service.userAgent}
119 disablewebsecurity={ 127 disablewebsecurity={
120 service.recipe.disablewebsecurity ? true : undefined 128 service.recipe.disablewebsecurity ? true : undefined
@@ -129,4 +137,4 @@ class ServiceWebview extends Component {
129 } 137 }
130} 138}
131 139
132export default observer(ServiceWebview); 140export default ServiceWebview;
diff --git a/src/components/services/content/Services.jsx b/src/components/services/content/Services.tsx
index da700b5b1..53cddd907 100644
--- a/src/components/services/content/Services.jsx
+++ b/src/components/services/content/Services.tsx
@@ -1,14 +1,13 @@
1import { Component } from 'react'; 1import { Component, ReactElement } from 'react';
2import PropTypes from 'prop-types'; 2import { observer } from 'mobx-react';
3import { observer, PropTypes as MobxPropTypes, inject } from 'mobx-react';
4import { Link } from 'react-router-dom'; 3import { Link } from 'react-router-dom';
5import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
6import Confetti from 'react-confetti'; 5import Confetti from 'react-confetti';
7import ms from 'ms'; 6import ms from 'ms';
8import injectSheet from 'react-jss'; 7import withStyles, { WithStylesProps } from 'react-jss';
9
10import ServiceView from './ServiceView'; 8import ServiceView from './ServiceView';
11import Appear from '../../ui/effects/Appear'; 9import Appear from '../../ui/effects/Appear';
10import Service from '../../../models/Service';
12 11
13const messages = defineMessages({ 12const messages = defineMessages({
14 getStarted: { 13 getStarted: {
@@ -39,37 +38,40 @@ const styles = {
39 }, 38 },
40}; 39};
41 40
42class Services extends Component { 41interface IProps extends WrappedComponentProps, WithStylesProps<typeof styles> {
43 static propTypes = { 42 services?: Service[];
44 services: MobxPropTypes.arrayOrObservableArray, 43 setWebviewReference: () => void;
45 setWebviewReference: PropTypes.func.isRequired, 44 detachService: () => void;
46 detachService: PropTypes.func.isRequired, 45 handleIPCMessage: () => void;
47 handleIPCMessage: PropTypes.func.isRequired, 46 openWindow: () => void;
48 openWindow: PropTypes.func.isRequired, 47 reload: (options: { serviceId: string }) => void;
49 reload: PropTypes.func.isRequired, 48 openSettings: (options: { path: string }) => void;
50 openSettings: PropTypes.func.isRequired, 49 update: (options: {
51 update: PropTypes.func.isRequired, 50 serviceId: string;
52 userHasCompletedSignup: PropTypes.bool.isRequired, 51 serviceData: { isEnabled: boolean };
53 // eslint-disable-next-line react/forbid-prop-types 52 redirect: boolean;
54 classes: PropTypes.object.isRequired, 53 }) => void;
55 isSpellcheckerEnabled: PropTypes.bool.isRequired, 54 userHasCompletedSignup: boolean;
56 }; 55 isSpellcheckerEnabled: boolean;
56}
57 57
58 static defaultProps = { 58interface IState {
59 services: [], 59 showConfetti: boolean;
60 }; 60}
61 61
62 _confettiTimeout = null; 62@observer
63class Services extends Component<IProps, IState> {
64 _confettiTimeout: number | null = null;
63 65
64 constructor() { 66 constructor(props: IProps) {
65 super(); 67 super(props);
66 68
67 this.state = { 69 this.state = {
68 showConfetti: true, 70 showConfetti: true,
69 }; 71 };
70 } 72 }
71 73
72 componentDidMount() { 74 componentDidMount(): void {
73 this._confettiTimeout = window.setTimeout(() => { 75 this._confettiTimeout = window.setTimeout(() => {
74 this.setState({ 76 this.setState({
75 showConfetti: false, 77 showConfetti: false,
@@ -77,15 +79,15 @@ class Services extends Component {
77 }, ms('8s')); 79 }, ms('8s'));
78 } 80 }
79 81
80 componentWillUnmount() { 82 componentWillUnmount(): void {
81 if (this._confettiTimeout) { 83 if (this._confettiTimeout) {
82 clearTimeout(this._confettiTimeout); 84 clearTimeout(this._confettiTimeout);
83 } 85 }
84 } 86 }
85 87
86 render() { 88 render(): ReactElement {
87 const { 89 const {
88 services, 90 services = [],
89 handleIPCMessage, 91 handleIPCMessage,
90 setWebviewReference, 92 setWebviewReference,
91 detachService, 93 detachService,
@@ -96,32 +98,31 @@ class Services extends Component {
96 userHasCompletedSignup, 98 userHasCompletedSignup,
97 classes, 99 classes,
98 isSpellcheckerEnabled, 100 isSpellcheckerEnabled,
101 intl,
99 } = this.props; 102 } = this.props;
100 103
101 const { showConfetti } = this.state; 104 const { showConfetti } = this.state;
102 105
103 const { intl } = this.props;
104
105 return ( 106 return (
106 <div className="services"> 107 <div className="services">
107 {userHasCompletedSignup && ( 108 {userHasCompletedSignup && (
108 <div className={classes.confettiContainer}> 109 <div className={classes.confettiContainer}>
109 <Confetti 110 <Confetti
110 width={window.width} 111 width={window.innerWidth}
111 height={window.height} 112 height={window.innerHeight}
112 numberOfPieces={showConfetti ? 200 : 0} 113 numberOfPieces={showConfetti ? 200 : 0}
113 /> 114 />
114 </div> 115 </div>
115 )} 116 )}
116 {services.length === 0 && ( 117 {services.length === 0 && (
117 <Appear timeout={1500} transitionName="slideUp"> 118 <Appear transitionName="slideUp">
118 <div className="services__no-service"> 119 <div className="services__no-service">
119 <img 120 <img
120 src="./assets/images/logo-beard-only.svg" 121 src="./assets/images/logo-beard-only.svg"
121 alt="Logo" 122 alt="Logo"
122 style={{ maxHeight: '50vh' }} 123 style={{ maxHeight: '50vh' }}
123 /> 124 />
124 <Appear timeout={300} transitionName="slideUp"> 125 <Appear transitionName="slideUp">
125 <Link to="/settings/recipes" className="button"> 126 <Link to="/settings/recipes" className="button">
126 {intl.formatMessage(messages.getStarted)} 127 {intl.formatMessage(messages.getStarted)}
127 </Link> 128 </Link>
@@ -158,8 +159,4 @@ class Services extends Component {
158 } 159 }
159} 160}
160 161
161export default injectIntl( 162export default injectIntl(withStyles(styles, { injectTheme: true })(Services));
162 injectSheet(styles, { injectTheme: true })(
163 inject('actions')(observer(Services)),
164 ),
165);
diff --git a/src/components/settings/SettingsLayout.jsx b/src/components/settings/SettingsLayout.tsx
index 989c428f2..3b706571e 100644
--- a/src/components/settings/SettingsLayout.jsx
+++ b/src/components/settings/SettingsLayout.tsx
@@ -1,8 +1,6 @@
1import { Component } from 'react'; 1import { Component, ReactElement } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 2import { observer } from 'mobx-react';
4import { defineMessages, injectIntl } from 'react-intl'; 3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
5
6import { mdiClose } from '@mdi/js'; 4import { mdiClose } from '@mdi/js';
7import { Outlet } from 'react-router-dom'; 5import { Outlet } from 'react-router-dom';
8import ErrorBoundary from '../util/ErrorBoundary'; 6import ErrorBoundary from '../util/ErrorBoundary';
@@ -17,35 +15,35 @@ const messages = defineMessages({
17 }, 15 },
18}); 16});
19 17
20class SettingsLayout extends Component { 18interface IProps extends WrappedComponentProps {
21 static propTypes = { 19 navigation: ReactElement;
22 navigation: PropTypes.element.isRequired, 20 closeSettings: () => void;
23 closeSettings: PropTypes.func.isRequired, 21}
24 }; 22
23@observer
24class SettingsLayout extends Component<IProps> {
25 constructor(props: IProps) {
26 super(props);
25 27
26 componentDidMount() { 28 this.handleKeyDown = this.handleKeyDown.bind(this);
27 document.addEventListener('keydown', this.handleKeyDown.bind(this), false);
28 } 29 }
29 30
30 componentWillUnmount() { 31 componentDidMount(): void {
31 document.removeEventListener( 32 document.addEventListener('keydown', this.handleKeyDown, false);
32 'keydown',
33 // eslint-disable-next-line unicorn/no-invalid-remove-event-listener
34 this.handleKeyDown.bind(this),
35 false,
36 );
37 } 33 }
38 34
39 handleKeyDown(e) { 35 componentWillUnmount(): void {
36 document.removeEventListener('keydown', this.handleKeyDown, false);
37 }
38
39 handleKeyDown(e: KeyboardEvent): void {
40 if (isEscKeyPress(e.keyCode)) { 40 if (isEscKeyPress(e.keyCode)) {
41 this.props.closeSettings(); 41 this.props.closeSettings();
42 } 42 }
43 } 43 }
44 44
45 render() { 45 render(): ReactElement {
46 const { navigation, closeSettings } = this.props; 46 const { navigation, closeSettings, intl } = this.props;
47
48 const { intl } = this.props;
49 47
50 return ( 48 return (
51 <Appear transitionName="fadeIn-fast"> 49 <Appear transitionName="fadeIn-fast">
@@ -59,7 +57,6 @@ class SettingsLayout extends Component {
59 /> 57 />
60 <div className="settings franz-form"> 58 <div className="settings franz-form">
61 {navigation} 59 {navigation}
62
63 <Outlet /> 60 <Outlet />
64 <button 61 <button
65 type="button" 62 type="button"
@@ -77,4 +74,4 @@ class SettingsLayout extends Component {
77 } 74 }
78} 75}
79 76
80export default injectIntl(observer(SettingsLayout)); 77export default injectIntl(SettingsLayout);
diff --git a/src/components/settings/settings/EditSettingsForm.tsx b/src/components/settings/settings/EditSettingsForm.tsx
index e796a48ec..8ccad9e49 100644
--- a/src/components/settings/settings/EditSettingsForm.tsx
+++ b/src/components/settings/settings/EditSettingsForm.tsx
@@ -1,5 +1,5 @@
1import { systemPreferences } from '@electron/remote'; 1import { systemPreferences } from '@electron/remote';
2import { Component } from 'react'; 2import { Component, ReactElement } from 'react';
3import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
4import prettyBytes from 'pretty-bytes'; 4import prettyBytes from 'pretty-bytes';
5import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; 5import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
@@ -9,7 +9,7 @@ import Button from '../../ui/button';
9import Toggle from '../../ui/toggle'; 9import Toggle from '../../ui/toggle';
10import Select from '../../ui/Select'; 10import Select from '../../ui/Select';
11import Input from '../../ui/input/index'; 11import Input from '../../ui/input/index';
12import ColorPickerInput from '../../ui/ColorPickerInput'; 12import ColorPickerInput from '../../ui/colorPickerInput';
13import Infobox from '../../ui/Infobox'; 13import Infobox from '../../ui/Infobox';
14import { H1, H2, H3, H5 } from '../../ui/headline'; 14import { H1, H2, H3, H5 } from '../../ui/headline';
15import { 15import {
@@ -258,14 +258,14 @@ const messages = defineMessages({
258 }, 258 },
259}); 259});
260 260
261const Hr = () => ( 261const Hr = (): ReactElement => (
262 <hr 262 <hr
263 className="settings__hr" 263 className="settings__hr"
264 style={{ marginBottom: 20, borderStyle: 'dashed' }} 264 style={{ marginBottom: 20, borderStyle: 'dashed' }}
265 /> 265 />
266); 266);
267 267
268const HrSections = () => ( 268const HrSections = (): ReactElement => (
269 <hr 269 <hr
270 className="settings__hr-sections" 270 className="settings__hr-sections"
271 style={{ marginTop: 20, marginBottom: 40, borderStyle: 'solid' }} 271 style={{ marginTop: 20, marginBottom: 40, borderStyle: 'solid' }}
@@ -325,8 +325,11 @@ class EditSettingsForm extends Component<IProps, IState> {
325 this.setState({ clearCacheButtonClicked: true }); 325 this.setState({ clearCacheButtonClicked: true });
326 }; 326 };
327 327
328 submit(e) { 328 submit(e): void {
329 e.preventDefault(); 329 if (e) {
330 e.preventDefault();
331 }
332
330 this.props.form.submit({ 333 this.props.form.submit({
331 onSuccess: form => { 334 onSuccess: form => {
332 const values = form.values(); 335 const values = form.values();
@@ -344,7 +347,7 @@ class EditSettingsForm extends Component<IProps, IState> {
344 }); 347 });
345 } 348 }
346 349
347 render() { 350 render(): ReactElement {
348 const { 351 const {
349 checkForUpdates, 352 checkForUpdates,
350 installUpdate, 353 installUpdate,
@@ -742,8 +745,8 @@ class EditSettingsForm extends Component<IProps, IState> {
742 {intl.formatMessage(messages.overallTheme)} 745 {intl.formatMessage(messages.overallTheme)}
743 <div className="settings__settings-group__apply-color"> 746 <div className="settings__settings-group__apply-color">
744 <ColorPickerInput 747 <ColorPickerInput
745 onChange={e => this.submit(e)} 748 {...form.$('accentColor').bind()}
746 field={form.$('accentColor')} 749 onColorChange={this.submit.bind(this)}
747 className="color-picker-input" 750 className="color-picker-input"
748 /> 751 />
749 </div> 752 </div>
@@ -752,8 +755,8 @@ class EditSettingsForm extends Component<IProps, IState> {
752 {intl.formatMessage(messages.progressbarTheme)} 755 {intl.formatMessage(messages.progressbarTheme)}
753 <div className="settings__settings-group__apply-color"> 756 <div className="settings__settings-group__apply-color">
754 <ColorPickerInput 757 <ColorPickerInput
755 onChange={e => this.submit(e)} 758 {...form.$('progressbarAccentColor').bind()}
756 field={form.$('progressbarAccentColor')} 759 onColorChange={this.submit.bind(this)}
757 className="color-picker-input" 760 className="color-picker-input"
758 /> 761 />
759 </div> 762 </div>
diff --git a/src/components/ui/ColorPickerInput.tsx b/src/components/ui/ColorPickerInput.tsx
deleted file mode 100644
index da1fffb71..000000000
--- a/src/components/ui/ColorPickerInput.tsx
+++ /dev/null
@@ -1,102 +0,0 @@
1import {
2 ChangeEvent,
3 ChangeEventHandler,
4 Component,
5 createRef,
6 RefObject,
7} from 'react';
8import { observer } from 'mobx-react';
9import classnames from 'classnames';
10import { SliderPicker } from 'react-color';
11import { noop } from 'lodash';
12import { Field } from '../../@types/mobx-form.types';
13
14interface IProps {
15 field: Field;
16 className?: string;
17 focus?: boolean;
18 onChange: ChangeEventHandler<HTMLInputElement>;
19}
20
21// TODO - [TS DEBT] check if field can be spread instead of having it single field attribute in interface
22@observer
23class ColorPickerInput extends Component<IProps> {
24 private inputElement: RefObject<HTMLInputElement> =
25 createRef<HTMLInputElement>();
26
27 componentDidMount() {
28 const { focus = false } = this.props;
29 if (focus) {
30 this.focus();
31 }
32 }
33
34 onChange(e: ChangeEvent<HTMLInputElement>) {
35 const { field, onChange = noop } = this.props;
36
37 onChange(e);
38 if (field.onChange) {
39 field.onChange(e);
40 }
41 }
42
43 focus() {
44 if (this.inputElement && this.inputElement.current) {
45 this.inputElement.current.focus();
46 }
47 }
48
49 handleChangeComplete = (color: { hex: string }) => {
50 const { field } = this.props;
51 field.value = color.hex;
52 };
53
54 render() {
55 const { field, className = null } = this.props;
56
57 let { type } = field;
58 type = 'text';
59
60 return (
61 <div
62 className={classnames({
63 'franz-form__field': true,
64 'has-error': field.error,
65 [`${className}`]: className,
66 })}
67 >
68 <SliderPicker
69 color={field.value}
70 onChangeComplete={this.handleChangeComplete}
71 id={field.id}
72 type={type}
73 className="franz-form__input"
74 name={field.name}
75 value={field.value}
76 placeholder={field.placeholder}
77 onBlur={field.onBlur}
78 onFocus={field.onFocus}
79 ref={this.inputElement}
80 disabled={field.disabled}
81 />
82 <div className="franz-form__input-wrapper franz-form__input-wrapper__color-picker">
83 <input
84 id={field.id}
85 type={type}
86 className="franz-form__input"
87 name={field.name}
88 value={field.value}
89 placeholder={field.placeholder}
90 onChange={e => this.onChange(e)}
91 onBlur={field.onBlur}
92 onFocus={field.onFocus}
93 ref={this.inputElement}
94 disabled={field.disabled}
95 />
96 </div>
97 </div>
98 );
99 }
100}
101
102export default ColorPickerInput;
diff --git a/src/components/ui/colorPickerInput/index.tsx b/src/components/ui/colorPickerInput/index.tsx
new file mode 100644
index 000000000..9bab6efec
--- /dev/null
+++ b/src/components/ui/colorPickerInput/index.tsx
@@ -0,0 +1,88 @@
1import {
2 Component,
3 createRef,
4 InputHTMLAttributes,
5 ReactElement,
6 RefObject,
7} from 'react';
8import { observer } from 'mobx-react';
9import classnames from 'classnames';
10import { SliderPicker } from 'react-color';
11import { noop } from 'lodash';
12import { FormFields } from '../../../@types/mobx-form.types';
13
14interface IProps extends InputHTMLAttributes<HTMLInputElement>, FormFields {
15 className?: string;
16 focus?: boolean;
17 onColorChange?: () => void;
18 error: string;
19}
20
21@observer
22class ColorPickerInput extends Component<IProps> {
23 private inputElement: RefObject<HTMLInputElement> =
24 createRef<HTMLInputElement>();
25
26 componentDidMount(): void {
27 const { focus = false } = this.props;
28 if (focus && this.inputElement && this.inputElement.current) {
29 this.inputElement.current.focus();
30 }
31 }
32
33 onChange({ hex }: { hex: string }): void {
34 const { onColorChange = noop, onChange = noop } = this.props;
35 onColorChange();
36 onChange(hex);
37 }
38
39 render(): ReactElement {
40 const {
41 id,
42 name,
43 value = '',
44 placeholder = '',
45 disabled = false,
46 className = null,
47 type = 'text',
48 error = '',
49 onChange = noop,
50 } = this.props;
51
52 return (
53 <div
54 className={classnames({
55 'franz-form__field': true,
56 'has-error': error,
57 [`${className}`]: className,
58 })}
59 ref={this.inputElement}
60 >
61 <SliderPicker
62 color={value}
63 onChange={this.onChange.bind(this)}
64 id={`${id}-SliderPicker`}
65 type={type}
66 className="franz-form__input"
67 name={name}
68 placeholder={placeholder}
69 disabled={disabled}
70 />
71 <div className="franz-form__input-wrapper franz-form__input-wrapper__color-picker">
72 <input
73 id={`${id}-Input`}
74 type={type}
75 className="franz-form__input"
76 name={name}
77 value={value}
78 placeholder={placeholder}
79 onChange={onChange}
80 disabled={disabled}
81 />
82 </div>
83 </div>
84 );
85 }
86}
87
88export default ColorPickerInput;
diff --git a/src/components/ui/effects/Appear.tsx b/src/components/ui/effects/Appear.tsx
index bf097b6a6..2076f6ba6 100644
--- a/src/components/ui/effects/Appear.tsx
+++ b/src/components/ui/effects/Appear.tsx
@@ -5,11 +5,22 @@ interface IProps {
5 children: ReactNode; 5 children: ReactNode;
6 transitionName?: string; 6 transitionName?: string;
7 className?: string; 7 className?: string;
8 transitionAppear?: boolean;
9 transitionLeave?: boolean;
10 transitionAppearTimeout?: number;
11 transitionEnterTimeout?: number;
12 transitionLeaveTimeout?: number;
8} 13}
14
9const Appear = ({ 15const Appear = ({
10 children, 16 children,
11 transitionName = 'fadeIn', 17 transitionName = 'fadeIn',
12 className = '', 18 className = '',
19 transitionAppear = true,
20 transitionLeave = true,
21 transitionAppearTimeout = 1500,
22 transitionEnterTimeout = 1500,
23 transitionLeaveTimeout = 1500,
13}: IProps): ReactElement | null => { 24}: IProps): ReactElement | null => {
14 const [mounted, setMounted] = useState(false); 25 const [mounted, setMounted] = useState(false);
15 26
@@ -24,11 +35,11 @@ const Appear = ({
24 return ( 35 return (
25 <CSSTransitionGroup 36 <CSSTransitionGroup
26 transitionName={transitionName} 37 transitionName={transitionName}
27 transitionAppear 38 transitionAppear={transitionAppear}
28 transitionLeave 39 transitionLeave={transitionLeave}
29 transitionAppearTimeout={1500} 40 transitionAppearTimeout={transitionAppearTimeout}
30 transitionEnterTimeout={1500} 41 transitionEnterTimeout={transitionEnterTimeout}
31 transitionLeaveTimeout={1500} 42 transitionLeaveTimeout={transitionLeaveTimeout}
32 className={className} 43 className={className}
33 > 44 >
34 {children} 45 {children}
diff --git a/src/containers/auth/ImportScreen.tsx b/src/containers/auth/ImportScreen.tsx
deleted file mode 100644
index 91e985ad5..000000000
--- a/src/containers/auth/ImportScreen.tsx
+++ /dev/null
@@ -1,25 +0,0 @@
1import { Component, ReactElement } from 'react';
2import { inject, observer } from 'mobx-react';
3import { StoresProps } from '../../@types/ferdium-components.types';
4import Import from '../../components/auth/Import';
5
6class ImportScreen extends Component<StoresProps> {
7 render(): ReactElement {
8 const { actions, stores } = this.props;
9
10 if (stores.user.isImportLegacyServicesCompleted) {
11 stores.router.push(stores.user.inviteRoute);
12 }
13
14 return (
15 <Import
16 services={stores.user.legacyServices}
17 onSubmit={actions.user.importLegacyServices}
18 isSubmitting={stores.user.isImportLegacyServicesExecuting}
19 inviteRoute={stores.user.inviteRoute}
20 />
21 );
22 }
23}
24
25export default inject('stores', 'actions')(observer(ImportScreen));
diff --git a/src/containers/auth/SetupAssistantScreen.tsx b/src/containers/auth/SetupAssistantScreen.tsx
index 661d688aa..0d4c3feec 100644
--- a/src/containers/auth/SetupAssistantScreen.tsx
+++ b/src/containers/auth/SetupAssistantScreen.tsx
@@ -1,88 +1,93 @@
1/* eslint-disable no-await-in-loop */
2import { Component, ReactElement } from 'react'; 1import { Component, ReactElement } from 'react';
3import { inject, observer } from 'mobx-react'; 2import { inject, observer } from 'mobx-react';
4
5import { StoresProps } from '../../@types/ferdium-components.types'; 3import { StoresProps } from '../../@types/ferdium-components.types';
6import sleep from '../../helpers/async-helpers'; 4import sleep from '../../helpers/async-helpers';
7import SetupAssistant from '../../components/auth/SetupAssistant'; 5import SetupAssistant from '../../components/auth/SetupAssistant';
6import { ILegacyServices } from '../../@types/legacy-types';
7
8interface IProps extends StoresProps {}
9
10interface IState {
11 isSettingUpServices: boolean;
12}
13
14@inject('stores', 'actions')
15@observer
16class SetupAssistantScreen extends Component<IProps, IState> {
17 services: ILegacyServices;
18
19 constructor(props: IProps) {
20 super(props);
8 21
9class SetupAssistantScreen extends Component<StoresProps> { 22 this.state = {
10 state = { 23 isSettingUpServices: false,
11 isSettingUpServices: false, 24 };
12 };
13 25
14 // TODO: Why are these hardcoded here? Do they need to conform to specific services in the packaged recipes? If so, its more important to fix this 26 // TODO: Why are these hardcoded here? Do they need to conform to specific services in the packaged recipes? If so, its more important to fix this
15 services = { 27 this.services = {
16 whatsapp: { 28 whatsapp: {
17 name: 'WhatsApp', 29 name: 'WhatsApp',
18 hasTeamId: false, 30 hasTeamId: false,
19 }, 31 },
20 messenger: { 32 messenger: {
21 name: 'Messenger', 33 name: 'Messenger',
22 hasTeamId: false, 34 hasTeamId: false,
23 }, 35 },
24 gmail: { 36 gmail: {
25 name: 'Gmail', 37 name: 'Gmail',
26 hasTeamId: false, 38 hasTeamId: false,
27 }, 39 },
28 skype: { 40 skype: {
29 name: 'Skype', 41 name: 'Skype',
30 hasTeamId: false, 42 hasTeamId: false,
31 }, 43 },
32 telegram: { 44 telegram: {
33 name: 'Telegram', 45 name: 'Telegram',
34 hasTeamId: false, 46 hasTeamId: false,
35 }, 47 },
36 instagram: { 48 instagram: {
37 name: 'Instagram', 49 name: 'Instagram',
38 hasTeamId: false, 50 hasTeamId: false,
39 }, 51 },
40 slack: { 52 slack: {
41 name: 'Slack', 53 name: 'Slack',
42 hasTeamId: true, 54 hasTeamId: true,
43 }, 55 },
44 hangouts: { 56 hangouts: {
45 name: 'Hangouts', 57 name: 'Hangouts',
46 hasTeamId: false, 58 hasTeamId: false,
47 }, 59 },
48 linkedin: { 60 linkedin: {
49 name: 'LinkedIn', 61 name: 'LinkedIn',
50 hasTeamId: false, 62 hasTeamId: false,
51 }, 63 },
52 }; 64 };
65 }
53 66
54 async setupServices(serviceConfig: any): Promise<void> { 67 async setupServices(serviceConfig: any): Promise<void> {
55 const { 68 const { services, router } = this.props.stores;
56 stores: { services, router },
57 } = this.props;
58 69
59 this.setState({ 70 this.setState({ isSettingUpServices: true });
60 isSettingUpServices: true,
61 });
62 71
63 // The store requests are not build for parallel requests so we need to finish one request after another 72 // The store requests are not build for parallel requests so we need to finish one request after another
64 for (const config of serviceConfig) { 73 for (const config of serviceConfig) {
65 const serviceData = { 74 // eslint-disable-next-line no-await-in-loop
66 name: this.services[config.id].name,
67 team: config.team,
68 };
69
70 await services._createService({ 75 await services._createService({
71 recipeId: config.id, 76 recipeId: config.id,
72 serviceData, 77 serviceData: {
78 name: this.services[config.id].name,
79 team: config.team,
80 },
73 redirect: false, 81 redirect: false,
74 skipCleanup: true, 82 skipCleanup: true,
75 }); 83 });
76 84
85 // eslint-disable-next-line no-await-in-loop
77 await sleep(100); 86 await sleep(100);
78 } 87 }
79 88
80 this.setState({ 89 this.setState({ isSettingUpServices: false });
81 isSettingUpServices: false,
82 });
83
84 await sleep(100); 90 await sleep(100);
85
86 router.push('/'); 91 router.push('/');
87 } 92 }
88 93
@@ -91,11 +96,11 @@ class SetupAssistantScreen extends Component<StoresProps> {
91 <SetupAssistant 96 <SetupAssistant
92 onSubmit={config => this.setupServices(config)} 97 onSubmit={config => this.setupServices(config)}
93 services={this.services} 98 services={this.services}
94 embed={false} 99 // embed={false} // TODO - [TS DEBT][PROP NOT USED IN COMPONENT] check legacy services type
95 isSettingUpServices={this.state.isSettingUpServices} 100 isSettingUpServices={this.state.isSettingUpServices}
96 /> 101 />
97 ); 102 );
98 } 103 }
99} 104}
100 105
101export default inject('stores', 'actions')(observer(SetupAssistantScreen)); 106export default SetupAssistantScreen;
diff --git a/src/features/workspaces/actions.ts b/src/features/workspaces/actions.ts
index 4c8b74450..b32bd7c86 100644
--- a/src/features/workspaces/actions.ts
+++ b/src/features/workspaces/actions.ts
@@ -7,6 +7,7 @@ export interface WorkspaceActions {
7 toggleWorkspaceDrawer: () => void; 7 toggleWorkspaceDrawer: () => void;
8 deactivate: () => void; 8 deactivate: () => void;
9 activate: (options: any) => void; 9 activate: (options: any) => void;
10 edit: ({ workspace }: { workspace: Workspace }) => void;
10} 11}
11 12
12export default createActionsFromDefinitions<WorkspaceActions>( 13export default createActionsFromDefinitions<WorkspaceActions>(
diff --git a/src/features/workspaces/components/WorkspaceDrawer.jsx b/src/features/workspaces/components/WorkspaceDrawer.tsx
index b0b0e639a..bdbebdb0a 100644
--- a/src/features/workspaces/components/WorkspaceDrawer.jsx
+++ b/src/features/workspaces/components/WorkspaceDrawer.tsx
@@ -1,18 +1,18 @@
1import { Component } from 'react'; 1import { Component, ReactElement } from 'react';
2import PropTypes from 'prop-types';
3import { observer } from 'mobx-react'; 2import { observer } from 'mobx-react';
4import injectSheet from 'react-jss'; 3import withStyles, { WithStylesProps } from 'react-jss';
5import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
6import ReactTooltip from 'react-tooltip'; 5import ReactTooltip from 'react-tooltip';
7
8import { mdiPlusBox, mdiCog } from '@mdi/js'; 6import { mdiPlusBox, mdiCog } from '@mdi/js';
9 7import { noop } from 'lodash';
10import { H1 } from '../../../components/ui/headline'; 8import { H1 } from '../../../components/ui/headline';
11import Icon from '../../../components/ui/icon'; 9import Icon from '../../../components/ui/icon';
12import WorkspaceDrawerItem from './WorkspaceDrawerItem'; 10import WorkspaceDrawerItem from './WorkspaceDrawerItem';
13import workspaceActions from '../actions'; 11import workspaceActions from '../actions';
14import { workspaceStore } from '../index'; 12import { workspaceStore } from '../index';
15import { getUserWorkspacesRequest } from '../api'; 13import { getUserWorkspacesRequest } from '../api';
14import Service from '../../../models/Service';
15import Workspace from '../models/Workspace';
16 16
17const messages = defineMessages({ 17const messages = defineMessages({
18 headline: { 18 headline: {
@@ -89,22 +89,22 @@ const styles = theme => ({
89 }, 89 },
90}); 90});
91 91
92class WorkspaceDrawer extends Component { 92interface IProps extends WithStylesProps<typeof styles>, WrappedComponentProps {
93 static propTypes = { 93 getServicesForWorkspace: (workspace: Workspace | null) => Service[];
94 classes: PropTypes.object.isRequired, 94}
95 getServicesForWorkspace: PropTypes.func.isRequired,
96 };
97 95
98 componentDidMount() { 96@observer
99 ReactTooltip.rebuild(); 97class WorkspaceDrawer extends Component<IProps> {
98 componentDidMount(): void {
100 try { 99 try {
100 ReactTooltip.rebuild();
101 getUserWorkspacesRequest.execute(); 101 getUserWorkspacesRequest.execute();
102 } catch (error) { 102 } catch (error) {
103 console.log(error); 103 console.log(error);
104 } 104 }
105 } 105 }
106 106
107 render() { 107 render(): ReactElement {
108 const { classes, getServicesForWorkspace } = this.props; 108 const { classes, getServicesForWorkspace } = this.props;
109 const { intl } = this.props; 109 const { intl } = this.props;
110 const { activeWorkspace, isSwitchingWorkspace, nextWorkspace, workspaces } = 110 const { activeWorkspace, isSwitchingWorkspace, nextWorkspace, workspaces } =
@@ -118,6 +118,7 @@ class WorkspaceDrawer extends Component {
118 {intl.formatMessage(messages.headline)} 118 {intl.formatMessage(messages.headline)}
119 <span 119 <span
120 className={classes.workspacesSettingsButton} 120 className={classes.workspacesSettingsButton}
121 onKeyDown={noop}
121 onClick={() => { 122 onClick={() => {
122 workspaceActions.openWorkspaceSettings(); 123 workspaceActions.openWorkspaceSettings();
123 }} 124 }}
@@ -165,6 +166,7 @@ class WorkspaceDrawer extends Component {
165 onClick={() => { 166 onClick={() => {
166 workspaceActions.openWorkspaceSettings(); 167 workspaceActions.openWorkspaceSettings();
167 }} 168 }}
169 onKeyDown={noop}
168 > 170 >
169 <Icon 171 <Icon
170 icon={mdiPlusBox} 172 icon={mdiPlusBox}
@@ -180,5 +182,5 @@ class WorkspaceDrawer extends Component {
180} 182}
181 183
182export default injectIntl( 184export default injectIntl(
183 injectSheet(styles, { injectTheme: true })(observer(WorkspaceDrawer)), 185 withStyles(styles, { injectTheme: true })(WorkspaceDrawer),
184); 186);
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 10c92216d..31b22847c 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -38,10 +38,6 @@
38 "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.", 38 "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.",
39 "global.userAgentPref": "User Agent", 39 "global.userAgentPref": "User Agent",
40 "global.yes": "Yes", 40 "global.yes": "Yes",
41 "import.headline": "Import your Ferdium 4 services",
42 "import.notSupportedHeadline": "Services not yet supported in Ferdium 5",
43 "import.skip.label": "I want to add services manually",
44 "import.submit.label": "Import {count} services",
45 "infobar.authRequestFailed": "There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.", 41 "infobar.authRequestFailed": "There were errors while trying to perform an authenticated request. Please try logging out and back in if this error persists.",
46 "infobar.buttonChangelog": "What is new?", 42 "infobar.buttonChangelog": "What is new?",
47 "infobar.buttonInstallUpdate": "Restart & install update", 43 "infobar.buttonInstallUpdate": "Restart & install update",
diff --git a/src/models/Recipe.ts b/src/models/Recipe.ts
index 8b4c7a8ba..9a28a59ac 100644
--- a/src/models/Recipe.ts
+++ b/src/models/Recipe.ts
@@ -60,6 +60,7 @@ export interface IRecipe {
60 author?: string[]; 60 author?: string[];
61 hasDarkMode?: boolean; 61 hasDarkMode?: boolean;
62 validateUrl?: (url: string) => boolean; 62 validateUrl?: (url: string) => boolean;
63 icons?: any;
63} 64}
64 65
65export default class Recipe implements IRecipe { 66export default class Recipe implements IRecipe {
diff --git a/src/routes.tsx b/src/routes.tsx
index 696d7762f..c0b637c3c 100644
--- a/src/routes.tsx
+++ b/src/routes.tsx
@@ -26,7 +26,6 @@ import AuthReleaseNotesScreen from './containers/auth/AuthReleaseNotesScreen';
26import PasswordScreen from './containers/auth/PasswordScreen'; 26import PasswordScreen from './containers/auth/PasswordScreen';
27import ChangeServerScreen from './containers/auth/ChangeServerScreen'; 27import ChangeServerScreen from './containers/auth/ChangeServerScreen';
28import SignupScreen from './containers/auth/SignupScreen'; 28import SignupScreen from './containers/auth/SignupScreen';
29import ImportScreen from './containers/auth/ImportScreen';
30import SetupAssistantScreen from './containers/auth/SetupAssistantScreen'; 29import SetupAssistantScreen from './containers/auth/SetupAssistantScreen';
31import InviteScreen from './containers/auth/InviteScreen'; 30import InviteScreen from './containers/auth/InviteScreen';
32import AuthLayoutContainer from './containers/auth/AuthLayoutContainer'; 31import AuthLayoutContainer from './containers/auth/AuthLayoutContainer';
@@ -81,10 +80,6 @@ class FerdiumRoutes extends Component<IProps> {
81 element={<SignupScreen {...routeProps} {...errorProps} />} 80 element={<SignupScreen {...routeProps} {...errorProps} />}
82 /> 81 />
83 <Route 82 <Route
84 path="/auth/signup/import"
85 element={<ImportScreen {...routeProps} />}
86 />
87 <Route
88 path="/auth/signup/setup" 83 path="/auth/signup/setup"
89 element={<SetupAssistantScreen {...routeProps} />} 84 element={<SetupAssistantScreen {...routeProps} />}
90 /> 85 />