aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar muhamedsalih-tw <104364298+muhamedsalih-tw@users.noreply.github.com>2022-11-02 06:31:36 +0530
committerLibravatar GitHub <noreply@github.com>2022-11-02 01:01:36 +0000
commit302d595f7c289387e53a0ef7df4d574ed4e25d70 (patch)
tree2385e59eaca9c78921d9b0b3681cfba1b3eef168 /src
parentRe-enable editing of the address bar to manually access a different url withi... (diff)
downloadferdium-app-302d595f7c289387e53a0ef7df4d574ed4e25d70.tar.gz
ferdium-app-302d595f7c289387e53a0ef7df4d574ed4e25d70.tar.zst
ferdium-app-302d595f7c289387e53a0ef7df4d574ed4e25d70.zip
Transform to TS and refactored components w.r.t deletion if duplicated Input component (#729)
Diffstat (limited to 'src')
-rw-r--r--src/@types/mobx-form.types.ts19
-rw-r--r--src/components/auth/ChangeServer.tsx7
-rw-r--r--src/components/auth/Invite.tsx (renamed from src/components/auth/Invite.js)127
-rw-r--r--src/components/auth/Locked.tsx (renamed from src/components/auth/Locked.js)65
-rw-r--r--src/components/auth/Login.tsx6
-rw-r--r--src/components/auth/Password.tsx (renamed from src/components/auth/Password.js)64
-rw-r--r--src/components/auth/Signup.tsx (renamed from src/components/auth/Signup.jsx)107
-rw-r--r--src/components/settings/services/EditServiceForm.js16
-rw-r--r--src/components/settings/settings/EditSettingsForm.tsx (renamed from src/components/settings/settings/EditSettingsForm.jsx)91
-rw-r--r--src/components/settings/user/EditUserForm.tsx (renamed from src/components/settings/user/EditUserForm.js)38
-rw-r--r--src/components/ui/ColorPickerInput.tsx18
-rw-r--r--src/components/ui/Input.tsx170
-rw-r--r--src/components/ui/input/index.tsx187
-rw-r--r--src/components/ui/input/styles.ts1
-rw-r--r--src/containers/auth/LockedScreen.tsx19
-rw-r--r--src/containers/settings/EditSettingsScreen.tsx15
-rw-r--r--src/features/basicAuth/Component.tsx (renamed from src/features/basicAuth/Component.js)45
-rw-r--r--src/features/basicAuth/store.ts12
-rw-r--r--src/features/publishDebugInfo/Component.tsx1
-rw-r--r--src/i18n/locales/en-US.json3
20 files changed, 450 insertions, 561 deletions
diff --git a/src/@types/mobx-form.types.ts b/src/@types/mobx-form.types.ts
index c9932c053..2a984d3a6 100644
--- a/src/@types/mobx-form.types.ts
+++ b/src/@types/mobx-form.types.ts
@@ -9,29 +9,22 @@ export interface FormFieldOptions {
9 9
10export interface FormFields { 10export interface FormFields {
11 fields: { 11 fields: {
12 [key: string]: { 12 [key: string]: Field;
13 label?: string;
14 placeholder?: string;
15 options?: FormFieldOptions[];
16 value?: string | boolean | number | null;
17 default?: string | boolean | number | null;
18 type?: string; // todo specifiy probably
19 disabled?: boolean;
20 validators?: any; // Not sure yet.
21 };
22 }; 13 };
23} 14}
24 15
25export interface Field extends Partial<Listeners> { 16export interface Field extends Listeners {
26 id?: string; 17 id?: string;
27 type?: string; 18 type?: string; // todo specifiy probably
28 name?: string; 19 name?: string;
29 value: string | string[]; 20 value?: any;
30 label?: string; 21 label?: string;
31 placeholder?: string; 22 placeholder?: string;
32 disabled?: boolean; 23 disabled?: boolean;
33 error?: GlobalError | string; 24 error?: GlobalError | string;
34 options?: SelectOptions[]; 25 options?: SelectOptions[];
26 default?: string | boolean | number | null;
27 validators?: any; // Not sure yet.
35} 28}
36 29
37export interface SelectOptions { 30export interface SelectOptions {
diff --git a/src/components/auth/ChangeServer.tsx b/src/components/auth/ChangeServer.tsx
index d8509f599..c49e52673 100644
--- a/src/components/auth/ChangeServer.tsx
+++ b/src/components/auth/ChangeServer.tsx
@@ -4,7 +4,7 @@ import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
4import { mdiArrowLeftCircle } from '@mdi/js'; 4import { mdiArrowLeftCircle } from '@mdi/js';
5import { noop } from 'lodash'; 5import { noop } from 'lodash';
6import Form from '../../lib/Form'; 6import Form from '../../lib/Form';
7import Input from '../ui/Input'; 7import Input from '../ui/input/index';
8import Select from '../ui/Select'; 8import Select from '../ui/Select';
9import Button from '../ui/button'; 9import Button from '../ui/button';
10import Link from '../ui/Link'; 10import Link from '../ui/Link';
@@ -128,7 +128,10 @@ class ChangeServer extends Component<IProps> {
128 )} 128 )}
129 <Select field={form.$('server')} /> 129 <Select field={form.$('server')} />
130 {!this.defaultServers.includes(form.$('server').value) && ( 130 {!this.defaultServers.includes(form.$('server').value) && (
131 <Input placeholder="Custom Server" field={form.$('customServer')} /> 131 <Input
132 placeholder="Custom Server"
133 {...form.$('customServer').bind()}
134 />
132 )} 135 )}
133 <Button 136 <Button
134 type="submit" 137 type="submit"
diff --git a/src/components/auth/Invite.js b/src/components/auth/Invite.tsx
index 6b0e0e40b..7723ea1ac 100644
--- a/src/components/auth/Invite.js
+++ b/src/components/auth/Invite.tsx
@@ -1,15 +1,14 @@
1import { Component, Fragment } 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 { Link } from 'react-router-dom'; 4import { Link } from 'react-router-dom';
6import classnames from 'classnames'; 5import classnames from 'classnames';
7 6import { noop } from 'lodash';
8import Infobox from '../ui/Infobox'; 7import Infobox from '../ui/Infobox';
9import Appear from '../ui/effects/Appear'; 8import Appear from '../ui/effects/Appear';
10import Form from '../../lib/Form'; 9import Form from '../../lib/Form';
11import { email } from '../../helpers/validation-helpers'; 10import { email, required } from '../../helpers/validation-helpers';
12import Input from '../ui/Input'; 11import Input from '../ui/input/index';
13import Button from '../ui/button'; 12import Button from '../ui/button';
14import { H1 } from '../ui/headline'; 13import { H1 } from '../ui/headline';
15 14
@@ -44,55 +43,61 @@ const messages = defineMessages({
44 }, 43 },
45}); 44});
46 45
47class Invite extends Component { 46interface IProps extends WrappedComponentProps {
48 static propTypes = { 47 onSubmit: (...args: any[]) => void;
49 onSubmit: PropTypes.func.isRequired, 48 embed?: boolean;
50 embed: PropTypes.bool, 49 isInviteSuccessful?: boolean;
51 isInviteSuccessful: PropTypes.bool, 50 isLoadingInvite?: boolean;
52 isLoadingInvite: PropTypes.bool, 51}
53 };
54
55 static defaultProps = {
56 embed: false,
57 isInviteSuccessful: false,
58 isLoadingInvite: false,
59 };
60 52
61 state = { showSuccessInfo: false }; 53interface IState {
54 showSuccessInfo: boolean;
55}
62 56
63 componentDidMount() { 57@observer
64 const { intl } = this.props; 58class Invite extends Component<IProps, IState> {
65 this.form = new Form( 59 form: Form;
66 { 60
67 fields: { 61 constructor(props: IProps) {
68 invite: [ 62 super(props);
69 ...Array.from({ length: 3 }).fill({ 63
70 fields: { 64 this.state = { showSuccessInfo: false };
71 name: { 65 this.form = new Form({
72 label: intl.formatMessage(messages.nameLabel), 66 fields: {
73 placeholder: intl.formatMessage(messages.nameLabel), 67 invite: [
74 onChange: () => { 68 ...Array.from({ length: 3 }).fill({
75 this.setState({ showSuccessInfo: false }); 69 fields: {
76 }, 70 name: {
77 // related: ['invite.0.email'], // path accepted but does not work 71 label: this.props.intl.formatMessage(messages.nameLabel),
72 placeholder: this.props.intl.formatMessage(messages.nameLabel),
73 onChange: () => {
74 this.setState({ showSuccessInfo: false });
78 }, 75 },
79 email: { 76 validators: [required],
80 label: intl.formatMessage(messages.emailLabel), 77 // related: ['invite.0.email'], // path accepted but does not work
81 placeholder: intl.formatMessage(messages.emailLabel), 78 },
82 onChange: () => { 79 email: {
83 this.setState({ showSuccessInfo: false }); 80 label: this.props.intl.formatMessage(messages.emailLabel),
84 }, 81 placeholder: this.props.intl.formatMessage(messages.emailLabel),
85 validators: [email], 82 onChange: () => {
83 this.setState({ showSuccessInfo: false });
86 }, 84 },
85 validators: [email],
87 }, 86 },
88 }), 87 },
89 ], 88 }),
90 }, 89 // TODO - [TS DEBT] need to fix this type once mobx-react-form is updated to next version
90 ] as any,
91 }, 91 },
92 intl, 92 });
93 ); 93 }
94 94
95 document.querySelector('input:first-child')?.focus(); 95 componentDidMount() {
96 const selector: HTMLElement | null =
97 document.querySelector('input:first-child');
98 if (selector) {
99 selector.focus();
100 }
96 } 101 }
97 102
98 submit(e) { 103 submit(e) {
@@ -101,10 +106,15 @@ class Invite extends Component {
101 this.form?.submit({ 106 this.form?.submit({
102 onSuccess: form => { 107 onSuccess: form => {
103 this.props.onSubmit({ invites: form.values().invite }); 108 this.props.onSubmit({ invites: form.values().invite });
104
105 this.form?.clear(); 109 this.form?.clear();
106 // this.form.$('invite.0.name').focus(); // path accepted but does not focus ;( 110 // this.form.$('invite.0.name').focus(); // path accepted but does not focus ;(
107 document.querySelector('input:first-child')?.focus(); 111
112 const selector: HTMLElement | null =
113 document.querySelector('input:first-child');
114 if (selector) {
115 selector.focus();
116 }
117
108 this.setState({ showSuccessInfo: true }); 118 this.setState({ showSuccessInfo: true });
109 }, 119 },
110 onError: () => {}, 120 onError: () => {},
@@ -114,7 +124,11 @@ class Invite extends Component {
114 render() { 124 render() {
115 const { form } = this; 125 const { form } = this;
116 const { intl } = this.props; 126 const { intl } = this.props;
117 const { embed, isInviteSuccessful, isLoadingInvite } = this.props; 127 const {
128 embed = false,
129 isInviteSuccessful = false,
130 isLoadingInvite = false,
131 } = this.props;
118 132
119 const atLeastOneEmailAddress = form 133 const atLeastOneEmailAddress = form
120 .$('invite') 134 .$('invite')
@@ -133,7 +147,7 @@ class Invite extends Component {
133 <Infobox 147 <Infobox
134 type="success" 148 type="success"
135 icon="checkbox-marked-circle-outline" 149 icon="checkbox-marked-circle-outline"
136 dismissable 150 dismissible
137 > 151 >
138 {intl.formatMessage(messages.inviteSuccessInfo)} 152 {intl.formatMessage(messages.inviteSuccessInfo)}
139 </Infobox> 153 </Infobox>
@@ -144,14 +158,14 @@ class Invite extends Component {
144 {!embed && ( 158 {!embed && (
145 <img src="./assets/images/logo.svg" className="auth__logo" alt="" /> 159 <img src="./assets/images/logo.svg" className="auth__logo" alt="" />
146 )} 160 )}
147 <H1 className={embed && 'invite__embed'}> 161 <H1 className={embed ? 'invite__embed' : ''}>
148 {intl.formatMessage(messages.headline)} 162 {intl.formatMessage(messages.headline)}
149 </H1> 163 </H1>
150 {form.$('invite').map(invite => ( 164 {form.$('invite').map(invite => (
151 <div className="grid" key={invite.key}> 165 <div className="grid" key={invite.key}>
152 <div className="grid__row"> 166 <div className="grid__row">
153 <Input field={invite.$('name')} showLabel={false} /> 167 <Input {...invite.$('name').bind()} showLabel={false} />
154 <Input field={invite.$('email')} showLabel={false} /> 168 <Input {...invite.$('email').bind()} showLabel={false} />
155 </div> 169 </div>
156 </div> 170 </div>
157 ))} 171 ))}
@@ -161,6 +175,7 @@ class Invite extends Component {
161 disabled={!atLeastOneEmailAddress} 175 disabled={!atLeastOneEmailAddress}
162 label={intl.formatMessage(messages.submitButtonLabel)} 176 label={intl.formatMessage(messages.submitButtonLabel)}
163 loaded={!isLoadingInvite} 177 loaded={!isLoadingInvite}
178 onClick={noop}
164 /> 179 />
165 {!embed && ( 180 {!embed && (
166 <Link 181 <Link
@@ -195,4 +210,4 @@ class Invite extends Component {
195 } 210 }
196} 211}
197 212
198export default injectIntl(observer(Invite)); 213export default injectIntl(Invite);
diff --git a/src/components/auth/Locked.js b/src/components/auth/Locked.tsx
index 6e32dd980..8b4e26878 100644
--- a/src/components/auth/Locked.js
+++ b/src/components/auth/Locked.tsx
@@ -1,17 +1,14 @@
1import { systemPreferences } from '@electron/remote'; 1import { systemPreferences } from '@electron/remote';
2import { Component } from 'react'; 2import { Component } from 'react';
3import PropTypes from 'prop-types';
4import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
5import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
6 5import { noop } from 'lodash';
7import Form from '../../lib/Form'; 6import Form from '../../lib/Form';
8import Input from '../ui/Input'; 7import Input from '../ui/input/index';
9import Button from '../ui/button'; 8import Button from '../ui/button';
10import { H1 } from '../ui/headline'; 9import { H1 } from '../ui/headline';
11import { isMac } from '../../environment'; 10import { isMac } from '../../environment';
12 11
13import { globalError as globalErrorPropType } from '../../prop-types';
14
15const messages = defineMessages({ 12const messages = defineMessages({
16 headline: { 13 headline: {
17 id: 'locked.headline', 14 id: 'locked.headline',
@@ -23,7 +20,7 @@ const messages = defineMessages({
23 }, 20 },
24 touchIdPrompt: { 21 touchIdPrompt: {
25 id: 'locked.touchIdPrompt', 22 id: 'locked.touchIdPrompt',
26 defaultMessage: 'unlock via Touch ID', 23 defaultMessage: 'Unlock with Touch ID',
27 }, 24 },
28 passwordLabel: { 25 passwordLabel: {
29 id: 'locked.password.label', 26 id: 'locked.password.label',
@@ -43,30 +40,31 @@ const messages = defineMessages({
43 }, 40 },
44}); 41});
45 42
46class Locked extends Component { 43interface IProps extends WrappedComponentProps {
47 static propTypes = { 44 onSubmit: (...args: any[]) => void;
48 onSubmit: PropTypes.func.isRequired, 45 unlock: () => void;
49 unlock: PropTypes.func.isRequired, 46 isSubmitting: boolean;
50 isSubmitting: PropTypes.bool.isRequired, 47 useTouchIdToUnlock: boolean;
51 useTouchIdToUnlock: PropTypes.bool.isRequired, 48 error: boolean;
52 error: globalErrorPropType.isRequired, 49}
53 };
54 50
55 form = (() => { 51@observer
56 const { intl } = this.props; 52class Locked extends Component<IProps> {
57 return new Form( 53 form: Form;
58 { 54
59 fields: { 55 constructor(props: IProps) {
60 password: { 56 super(props);
61 label: intl.formatMessage(messages.passwordLabel), 57
62 value: '', 58 this.form = new Form({
63 type: 'password', 59 fields: {
64 }, 60 password: {
61 label: this.props.intl.formatMessage(messages.passwordLabel),
62 value: '',
63 type: 'password',
65 }, 64 },
66 }, 65 },
67 intl, 66 });
68 ); 67 }
69 })();
70 68
71 submit(e) { 69 submit(e) {
72 e.preventDefault(); 70 e.preventDefault();
@@ -90,8 +88,7 @@ class Locked extends Component {
90 88
91 render() { 89 render() {
92 const { form } = this; 90 const { form } = this;
93 const { intl } = this.props; 91 const { isSubmitting, error, useTouchIdToUnlock, intl } = this.props;
94 const { isSubmitting, error, useTouchIdToUnlock } = this.props;
95 92
96 const touchIdEnabled = isMac 93 const touchIdEnabled = isMac
97 ? useTouchIdToUnlock && systemPreferences.canPromptTouchID() 94 ? useTouchIdToUnlock && systemPreferences.canPromptTouchID()
@@ -118,8 +115,8 @@ class Locked extends Component {
118 </> 115 </>
119 )} 116 )}
120 117
121 <Input field={form.$('password')} showPasswordToggle focus /> 118 <Input {...form.$('password').bind()} showPasswordToggle focus />
122 {error.code === 'invalid-credentials' && ( 119 {error && (
123 <p className="error-message center"> 120 <p className="error-message center">
124 {intl.formatMessage(messages.invalidCredentials)} 121 {intl.formatMessage(messages.invalidCredentials)}
125 </p> 122 </p>
@@ -130,6 +127,7 @@ class Locked extends Component {
130 buttonType="secondary" 127 buttonType="secondary"
131 label={`${submitButtonLabel} ...`} 128 label={`${submitButtonLabel} ...`}
132 loaded={false} 129 loaded={false}
130 onClick={noop}
133 disabled 131 disabled
134 /> 132 />
135 ) : ( 133 ) : (
@@ -137,6 +135,7 @@ class Locked extends Component {
137 type="submit" 135 type="submit"
138 className="auth__button" 136 className="auth__button"
139 label={submitButtonLabel} 137 label={submitButtonLabel}
138 onClick={noop}
140 /> 139 />
141 )} 140 )}
142 </form> 141 </form>
@@ -145,4 +144,4 @@ class Locked extends Component {
145 } 144 }
146} 145}
147 146
148export default injectIntl(observer(Locked)); 147export default injectIntl(Locked);
diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx
index 66a567fe4..185a6ad48 100644
--- a/src/components/auth/Login.tsx
+++ b/src/components/auth/Login.tsx
@@ -9,7 +9,7 @@ import { API_VERSION } from '../../environment-remote';
9import { serverBase } from '../../api/apiBase'; // TODO: Remove this line after fixing password recovery in-app 9import { serverBase } from '../../api/apiBase'; // TODO: Remove this line after fixing password recovery in-app
10import Form from '../../lib/Form'; 10import Form from '../../lib/Form';
11import { required, email } from '../../helpers/validation-helpers'; 11import { required, email } from '../../helpers/validation-helpers';
12import Input from '../ui/Input'; 12import Input from '../ui/input/index';
13import Button from '../ui/button'; 13import Button from '../ui/button';
14import Link from '../ui/Link'; 14import Link from '../ui/Link';
15import { H1 } from '../ui/headline'; 15import { H1 } from '../ui/headline';
@@ -136,8 +136,8 @@ class Login extends Component<IProps> {
136 {intl.formatMessage(messages.serverLogout)} 136 {intl.formatMessage(messages.serverLogout)}
137 </p> 137 </p>
138 )} 138 )}
139 <Input field={form.$('email')} focus /> 139 <Input {...form.$('email').bind()} focus />
140 <Input field={form.$('password')} showPasswordToggle /> 140 <Input {...form.$('password').bind()} showPasswordToggle />
141 {error.code === 'invalid-credentials' && ( 141 {error.code === 'invalid-credentials' && (
142 <> 142 <>
143 <p className="error-message center"> 143 <p className="error-message center">
diff --git a/src/components/auth/Password.js b/src/components/auth/Password.tsx
index 5086b0bbd..53fdbf842 100644
--- a/src/components/auth/Password.js
+++ b/src/components/auth/Password.tsx
@@ -1,11 +1,11 @@
1import { Component } from 'react'; 1import { Component, FormEvent } from 'react';
2import PropTypes from 'prop-types'; 2import { observer } from 'mobx-react';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
4import { defineMessages, injectIntl } from 'react-intl';
5 4
5import { noop } from 'lodash';
6import Form from '../../lib/Form'; 6import Form from '../../lib/Form';
7import { required, email } from '../../helpers/validation-helpers'; 7import { required, email } from '../../helpers/validation-helpers';
8import Input from '../ui/Input'; 8import Input from '../ui/input/index';
9import Button from '../ui/button'; 9import Button from '../ui/button';
10import Link from '../ui/Link'; 10import Link from '../ui/Link';
11import Infobox from '../ui/Infobox'; 11import Infobox from '../ui/Infobox';
@@ -39,32 +39,33 @@ const messages = defineMessages({
39 }, 39 },
40}); 40});
41 41
42class Password extends Component { 42interface IProps extends WrappedComponentProps {
43 static propTypes = { 43 onSubmit: (...args: any[]) => void;
44 onSubmit: PropTypes.func.isRequired, 44 isSubmitting: boolean;
45 isSubmitting: PropTypes.bool.isRequired, 45 signupRoute: string;
46 signupRoute: PropTypes.string.isRequired, 46 loginRoute: string;
47 loginRoute: PropTypes.string.isRequired, 47 status: string[];
48 status: MobxPropTypes.arrayOrObservableArray.isRequired, 48}
49 }; 49
50@observer
51class Password extends Component<IProps> {
52 form: Form;
53
54 constructor(props: IProps) {
55 super(props);
50 56
51 form = (() => { 57 this.form = new Form({
52 const { intl } = this.props; 58 fields: {
53 return new Form( 59 email: {
54 { 60 label: this.props.intl.formatMessage(messages.emailLabel),
55 fields: { 61 value: '',
56 email: { 62 validators: [required, email],
57 label: intl.formatMessage(messages.emailLabel),
58 value: '',
59 validators: [required, email],
60 },
61 }, 63 },
62 }, 64 },
63 intl, 65 });
64 ); 66 }
65 })();
66 67
67 submit(e) { 68 submit(e: FormEvent<HTMLFormElement>): void {
68 e.preventDefault(); 69 e.preventDefault();
69 this.form.submit({ 70 this.form.submit({
70 onSuccess: form => { 71 onSuccess: form => {
@@ -76,8 +77,7 @@ class Password extends Component {
76 77
77 render() { 78 render() {
78 const { form } = this; 79 const { form } = this;
79 const { intl } = this.props; 80 const { isSubmitting, signupRoute, loginRoute, status, intl } = this.props;
80 const { isSubmitting, signupRoute, loginRoute, status } = this.props;
81 81
82 return ( 82 return (
83 <div className="auth__container"> 83 <div className="auth__container">
@@ -91,7 +91,7 @@ class Password extends Component {
91 {intl.formatMessage(messages.successInfo)} 91 {intl.formatMessage(messages.successInfo)}
92 </Infobox> 92 </Infobox>
93 )} 93 )}
94 <Input field={form.$('email')} focus /> 94 <Input {...form.$('email').bind()} focus />
95 {status.length > 0 && status.includes('no-user') && ( 95 {status.length > 0 && status.includes('no-user') && (
96 <p className="error-message center"> 96 <p className="error-message center">
97 {intl.formatMessage(messages.noUser)} 97 {intl.formatMessage(messages.noUser)}
@@ -103,6 +103,7 @@ class Password extends Component {
103 buttonType="secondary" 103 buttonType="secondary"
104 label={`${intl.formatMessage(globalMessages.submit)} ...`} 104 label={`${intl.formatMessage(globalMessages.submit)} ...`}
105 loaded={false} 105 loaded={false}
106 onClick={noop}
106 disabled 107 disabled
107 /> 108 />
108 ) : ( 109 ) : (
@@ -110,6 +111,7 @@ class Password extends Component {
110 type="submit" 111 type="submit"
111 className="auth__button" 112 className="auth__button"
112 label={intl.formatMessage(globalMessages.submit)} 113 label={intl.formatMessage(globalMessages.submit)}
114 onClick={noop}
113 /> 115 />
114 )} 116 )}
115 </form> 117 </form>
@@ -124,4 +126,4 @@ class Password extends Component {
124 } 126 }
125} 127}
126 128
127export default injectIntl(observer(Password)); 129export default injectIntl(Password);
diff --git a/src/components/auth/Signup.jsx b/src/components/auth/Signup.tsx
index e0337656c..af14430aa 100644
--- a/src/components/auth/Signup.jsx
+++ b/src/components/auth/Signup.tsx
@@ -1,20 +1,18 @@
1/* eslint jsx-a11y/anchor-is-valid: 0 */
2import { Component } from 'react'; 1import { Component } from 'react';
3import PropTypes from 'prop-types';
4import { observer, inject } from 'mobx-react'; 2import { observer, inject } from 'mobx-react';
5import { defineMessages, injectIntl } from 'react-intl'; 3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
6
7import { mdiArrowLeftCircle } from '@mdi/js'; 4import { mdiArrowLeftCircle } from '@mdi/js';
5import { noop } from 'lodash';
8import Form from '../../lib/Form'; 6import Form from '../../lib/Form';
9import { required, email, minLength } from '../../helpers/validation-helpers'; 7import { required, email, minLength } from '../../helpers/validation-helpers';
10import Input from '../ui/Input'; 8import Input from '../ui/input/index';
11import Button from '../ui/button'; 9import Button from '../ui/button';
12import Link from '../ui/Link'; 10import Link from '../ui/Link';
13import Icon from '../ui/icon'; 11import Icon from '../ui/icon';
14
15import { globalError as globalErrorPropType } from '../../prop-types';
16import { serverBase } from '../../api/apiBase'; 12import { serverBase } from '../../api/apiBase';
17import { H1 } from '../ui/headline'; 13import { H1 } from '../ui/headline';
14import { GlobalError } from '../../@types/ferdium-components.types';
15import { Actions } from '../../actions/lib/actions';
18 16
19const messages = defineMessages({ 17const messages = defineMessages({
20 headline: { 18 headline: {
@@ -33,10 +31,10 @@ const messages = defineMessages({
33 id: 'signup.email.label', 31 id: 'signup.email.label',
34 defaultMessage: 'Email address', 32 defaultMessage: 'Email address',
35 }, 33 },
36 // companyLabel: { 34 companyLabel: {
37 // id: 'signup.company.label', 35 id: 'signup.company.label',
38 // defaultMessage: 'Company', 36 defaultMessage: 'Company',
39 // }, 37 },
40 passwordLabel: { 38 passwordLabel: {
41 id: 'signup.password.label', 39 id: 'signup.password.label',
42 defaultMessage: 'Password', 40 defaultMessage: 'Password',
@@ -67,45 +65,48 @@ const messages = defineMessages({
67 }, 65 },
68}); 66});
69 67
70class Signup extends Component { 68interface IProps extends WrappedComponentProps {
71 static propTypes = { 69 onSubmit: (...args: any[]) => void;
72 onSubmit: PropTypes.func.isRequired, 70 isSubmitting: boolean;
73 isSubmitting: PropTypes.bool.isRequired, 71 loginRoute: string;
74 loginRoute: PropTypes.string.isRequired, 72 error: GlobalError;
75 error: globalErrorPropType.isRequired, 73 actions?: Actions;
76 }; 74}
77 75
78 form = (() => { 76@inject('actions')
79 const { intl } = this.props; 77@observer
80 return new Form( 78class Signup extends Component<IProps> {
81 { 79 form: Form;
82 fields: { 80
83 firstname: { 81 constructor(props: IProps) {
84 label: intl.formatMessage(messages.firstnameLabel), 82 super(props);
85 value: '', 83
86 validators: [required], 84 this.form = new Form({
87 }, 85 fields: {
88 lastname: { 86 firstname: {
89 label: intl.formatMessage(messages.lastnameLabel), 87 label: this.props.intl.formatMessage(messages.firstnameLabel),
90 value: '', 88 value: '',
91 validators: [required], 89 validators: [required],
92 }, 90 },
93 email: { 91 lastname: {
94 label: intl.formatMessage(messages.emailLabel), 92 label: this.props.intl.formatMessage(messages.lastnameLabel),
95 value: '', 93 value: '',
96 validators: [required, email], 94 validators: [required],
97 }, 95 },
98 password: { 96 email: {
99 label: intl.formatMessage(messages.passwordLabel), 97 label: this.props.intl.formatMessage(messages.emailLabel),
100 value: '', 98 value: '',
101 validators: [required, minLength(6)], 99 validators: [required, email],
102 type: 'password', 100 },
103 }, 101 password: {
102 label: this.props.intl.formatMessage(messages.passwordLabel),
103 value: '',
104 validators: [required, minLength(6)],
105 type: 'password',
104 }, 106 },
105 }, 107 },
106 intl, 108 });
107 ); 109 }
108 })();
109 110
110 submit(e) { 111 submit(e) {
111 e.preventDefault(); 112 e.preventDefault();
@@ -138,12 +139,12 @@ class Signup extends Component {
138 </Link> 139 </Link>
139 <H1>{intl.formatMessage(messages.headline)}</H1> 140 <H1>{intl.formatMessage(messages.headline)}</H1>
140 <div className="grid__row"> 141 <div className="grid__row">
141 <Input field={form.$('firstname')} focus /> 142 <Input {...form.$('firstname').bind()} focus />
142 <Input field={form.$('lastname')} /> 143 <Input {...form.$('lastname').bind()} />
143 </div> 144 </div>
144 <Input field={form.$('email')} /> 145 <Input {...form.$('email').bind()} />
145 <Input 146 <Input
146 field={form.$('password')} 147 {...form.$('password').bind()}
147 showPasswordToggle 148 showPasswordToggle
148 scorePassword 149 scorePassword
149 /> 150 />
@@ -158,12 +159,14 @@ class Signup extends Component {
158 label={`${intl.formatMessage(messages.submitButtonLabel)} ...`} 159 label={`${intl.formatMessage(messages.submitButtonLabel)} ...`}
159 loaded={false} 160 loaded={false}
160 disabled 161 disabled
162 onClick={noop}
161 /> 163 />
162 ) : ( 164 ) : (
163 <Button 165 <Button
164 type="submit" 166 type="submit"
165 className="auth__button" 167 className="auth__button"
166 label={intl.formatMessage(messages.submitButtonLabel)} 168 label={intl.formatMessage(messages.submitButtonLabel)}
169 onClick={noop}
167 /> 170 />
168 )} 171 )}
169 <p className="legal"> 172 <p className="legal">
@@ -203,4 +206,4 @@ class Signup extends Component {
203 } 206 }
204} 207}
205 208
206export default injectIntl(inject('actions')(observer(Signup))); 209export default injectIntl(Signup);
diff --git a/src/components/settings/services/EditServiceForm.js b/src/components/settings/services/EditServiceForm.js
index 069b6643c..eeb9ef54d 100644
--- a/src/components/settings/services/EditServiceForm.js
+++ b/src/components/settings/services/EditServiceForm.js
@@ -11,7 +11,7 @@ import Recipe from '../../../models/Recipe';
11import Service from '../../../models/Service'; 11import Service from '../../../models/Service';
12import Tabs from '../../ui/Tabs/Tabs'; 12import Tabs from '../../ui/Tabs/Tabs';
13import TabItem from '../../ui/Tabs/TabItem'; 13import TabItem from '../../ui/Tabs/TabItem';
14import Input from '../../ui/Input'; 14import Input from '../../ui/input/index';
15import Toggle from '../../ui/Toggle'; 15import Toggle from '../../ui/Toggle';
16import Slider from '../../ui/Slider'; 16import Slider from '../../ui/Slider';
17import Button from '../../ui/button'; 17import Button from '../../ui/button';
@@ -292,7 +292,7 @@ class EditServiceForm extends Component {
292 <div className="settings__body"> 292 <div className="settings__body">
293 <form onSubmit={e => this.submit(e)} id="form"> 293 <form onSubmit={e => this.submit(e)} id="form">
294 <div className="service-name"> 294 <div className="service-name">
295 <Input field={form.$('name')} focus /> 295 <Input {...form.$('name').bind()} focus />
296 </div> 296 </div>
297 {(recipe.hasTeamId || recipe.hasCustomUrl) && ( 297 {(recipe.hasTeamId || recipe.hasCustomUrl) && (
298 <Tabs active={activeTabIndex}> 298 <Tabs active={activeTabIndex}>
@@ -314,7 +314,7 @@ class EditServiceForm extends Component {
314 )} 314 )}
315 {recipe.hasCustomUrl && ( 315 {recipe.hasCustomUrl && (
316 <TabItem title={intl.formatMessage(messages.tabOnPremise)}> 316 <TabItem title={intl.formatMessage(messages.tabOnPremise)}>
317 <Input field={form.$('customUrl')} /> 317 <Input {...form.$('customUrl').bind()} />
318 {form.error === 'url-validation-error' && ( 318 {form.error === 'url-validation-error' && (
319 <p className="franz-form__error"> 319 <p className="franz-form__error">
320 {intl.formatMessage(messages.customUrlValidationError, { 320 {intl.formatMessage(messages.customUrlValidationError, {
@@ -435,17 +435,17 @@ class EditServiceForm extends Component {
435 <div className="grid"> 435 <div className="grid">
436 <div className="grid__row"> 436 <div className="grid__row">
437 <Input 437 <Input
438 field={form.$('proxy.host')} 438 {...form.$('proxy.host').bind()}
439 className="proxyHost" 439 className="proxyHost"
440 /> 440 />
441 <Input field={form.$('proxy.port')} /> 441 <Input {...form.$('proxy.port').bind()} />
442 </div> 442 </div>
443 </div> 443 </div>
444 <div className="grid"> 444 <div className="grid">
445 <div className="grid__row"> 445 <div className="grid__row">
446 <Input field={form.$('proxy.user')} /> 446 <Input {...form.$('proxy.user').bind()} />
447 <Input 447 <Input
448 field={form.$('proxy.password')} 448 {...form.$('proxy.password').bind()}
449 showPasswordToggle 449 showPasswordToggle
450 /> 450 />
451 </div> 451 </div>
@@ -464,7 +464,7 @@ class EditServiceForm extends Component {
464 )} 464 )}
465 465
466 <div className="user-agent"> 466 <div className="user-agent">
467 <Input field={form.$('userAgentPref')} /> 467 <Input {...form.$('userAgentPref').bind()} />
468 <p className="settings__help"> 468 <p className="settings__help">
469 {intl.formatMessage(globalMessages.userAgentHelp)} 469 {intl.formatMessage(globalMessages.userAgentHelp)}
470 </p> 470 </p>
diff --git a/src/components/settings/settings/EditSettingsForm.jsx b/src/components/settings/settings/EditSettingsForm.tsx
index a327dccac..5c422b977 100644
--- a/src/components/settings/settings/EditSettingsForm.jsx
+++ b/src/components/settings/settings/EditSettingsForm.tsx
@@ -1,17 +1,14 @@
1import { systemPreferences } from '@electron/remote'; 1import { systemPreferences } from '@electron/remote';
2import { Component } from 'react'; 2import { Component } from 'react';
3import PropTypes from 'prop-types';
4import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
5import prettyBytes from 'pretty-bytes'; 4import prettyBytes from 'pretty-bytes';
6import { defineMessages, injectIntl } from 'react-intl'; 5import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
7
8import { mdiGithub, mdiOpenInNew, mdiPowerPlug } from '@mdi/js'; 6import { mdiGithub, mdiOpenInNew, mdiPowerPlug } from '@mdi/js';
9
10import Form from '../../../lib/Form'; 7import Form from '../../../lib/Form';
11import Button from '../../ui/button'; 8import Button from '../../ui/button';
12import Toggle from '../../ui/Toggle'; 9import Toggle from '../../ui/Toggle.js';
13import Select from '../../ui/Select'; 10import Select from '../../ui/Select';
14import Input from '../../ui/Input'; 11import Input from '../../ui/input/index';
15import ColorPickerInput from '../../ui/ColorPickerInput'; 12import ColorPickerInput from '../../ui/ColorPickerInput';
16import Infobox from '../../ui/Infobox'; 13import Infobox from '../../ui/Infobox';
17import { H1, H2, H3, H5 } from '../../ui/headline'; 14import { H1, H2, H3, H5 } from '../../ui/headline';
@@ -20,9 +17,7 @@ import {
20 userDataPath, 17 userDataPath,
21 userDataRecipesPath, 18 userDataRecipesPath,
22} from '../../../environment-remote'; 19} from '../../../environment-remote';
23
24import { updateVersionParse } from '../../../helpers/update-helpers'; 20import { updateVersionParse } from '../../../helpers/update-helpers';
25
26import { 21import {
27 DEFAULT_ACCENT_COLOR, 22 DEFAULT_ACCENT_COLOR,
28 DEFAULT_APP_SETTINGS, 23 DEFAULT_APP_SETTINGS,
@@ -264,6 +259,7 @@ const Hr = () => (
264 style={{ marginBottom: 20, borderStyle: 'dashed' }} 259 style={{ marginBottom: 20, borderStyle: 'dashed' }}
265 /> 260 />
266); 261);
262
267const HrSections = () => ( 263const HrSections = () => (
268 <hr 264 <hr
269 className="settings__hr-sections" 265 className="settings__hr-sections"
@@ -271,32 +267,41 @@ const HrSections = () => (
271 /> 267 />
272); 268);
273 269
274class EditSettingsForm extends Component { 270interface IProps extends WrappedComponentProps {
275 static propTypes = { 271 form: Form;
276 checkForUpdates: PropTypes.func.isRequired, 272 isCheckingForUpdates: boolean;
277 installUpdate: PropTypes.func.isRequired, 273 isUpdateAvailable: boolean;
278 form: PropTypes.instanceOf(Form).isRequired, 274 noUpdateAvailable: boolean;
279 onSubmit: PropTypes.func.isRequired, 275 updateIsReadyToInstall: boolean;
280 isCheckingForUpdates: PropTypes.bool.isRequired, 276 updateFailed: boolean;
281 isUpdateAvailable: PropTypes.bool.isRequired, 277 isClearingAllCache: boolean;
282 noUpdateAvailable: PropTypes.bool.isRequired, 278 isTodosActivated: boolean;
283 updateIsReadyToInstall: PropTypes.bool.isRequired, 279 automaticUpdates: boolean;
284 updateFailed: PropTypes.bool.isRequired, 280 isDarkmodeEnabled: boolean;
285 isClearingAllCache: PropTypes.bool.isRequired, 281 isAdaptableDarkModeEnabled: boolean;
286 onClearAllCache: PropTypes.func.isRequired, 282 isUseGrayscaleServicesEnabled: boolean;
287 getCacheSize: PropTypes.func.isRequired, 283 lockingFeatureEnabled: boolean;
288 isTodosActivated: PropTypes.bool.isRequired, 284 isSplitModeEnabled: boolean;
289 automaticUpdates: PropTypes.bool.isRequired, 285 isOnline: boolean;
290 isDarkmodeEnabled: PropTypes.bool.isRequired, 286 showServicesUpdatedInfoBar: boolean;
291 isAdaptableDarkModeEnabled: PropTypes.bool.isRequired, 287 updateVersion: string;
292 isUseGrayscaleServicesEnabled: PropTypes.bool.isRequired, 288 serverURL: string;
293 openProcessManager: PropTypes.func.isRequired, 289 onClearAllCache: () => void;
294 isSplitModeEnabled: PropTypes.bool.isRequired, 290 getCacheSize: () => void;
295 isOnline: PropTypes.bool.isRequired, 291 checkForUpdates: () => void;
296 serverURL: PropTypes.string.isRequired, 292 installUpdate: () => void;
297 }; 293 openProcessManager: () => void;
294 onSubmit: (...args: any[]) => void;
295}
296
297interface IState {
298 activeSetttingsTab: string;
299 clearCacheButtonClicked: boolean;
300}
298 301
299 constructor(props) { 302@observer
303class EditSettingsForm extends Component<IProps, IState> {
304 constructor(props: IProps) {
300 super(props); 305 super(props);
301 306
302 this.state = { 307 this.state = {
@@ -358,8 +363,8 @@ class EditSettingsForm extends Component {
358 isTodosActivated, 363 isTodosActivated,
359 isOnline, 364 isOnline,
360 serverURL, 365 serverURL,
366 intl,
361 } = this.props; 367 } = this.props;
362 const { intl } = this.props;
363 368
364 let updateButtonLabelMessage = messages.buttonSearchForUpdate; 369 let updateButtonLabelMessage = messages.buttonSearchForUpdate;
365 if (isCheckingForUpdates) { 370 if (isCheckingForUpdates) {
@@ -520,7 +525,7 @@ class EditSettingsForm extends Component {
520 <Toggle field={form.$('reloadAfterResume')} /> 525 <Toggle field={form.$('reloadAfterResume')} />
521 {reloadAfterResume && ( 526 {reloadAfterResume && (
522 <div> 527 <div>
523 <Input field={form.$('reloadAfterResumeTime')} /> 528 <Input {...form.$('reloadAfterResumeTime').bind()} />
524 <Hr /> 529 <Hr />
525 </div> 530 </div>
526 )} 531 )}
@@ -541,7 +546,7 @@ class EditSettingsForm extends Component {
541 <Input 546 <Input
542 placeholder="Todo Server" 547 placeholder="Todo Server"
543 onChange={e => this.submit(e)} 548 onChange={e => this.submit(e)}
544 field={form.$('customTodoServer')} 549 {...form.$('customTodoServer').bind()}
545 /> 550 />
546 <p 551 <p
547 className="settings__message" 552 className="settings__message"
@@ -580,7 +585,7 @@ class EditSettingsForm extends Component {
580 <Input 585 <Input
581 placeholder="17:00" 586 placeholder="17:00"
582 onChange={e => this.submit(e)} 587 onChange={e => this.submit(e)}
583 field={form.$('scheduledDNDStart')} 588 {...form.$('scheduledDNDStart').bind()}
584 type="time" 589 type="time"
585 /> 590 />
586 </div> 591 </div>
@@ -593,7 +598,7 @@ class EditSettingsForm extends Component {
593 <Input 598 <Input
594 placeholder="09:00" 599 placeholder="09:00"
595 onChange={e => this.submit(e)} 600 onChange={e => this.submit(e)}
596 field={form.$('scheduledDNDEnd')} 601 {...form.$('scheduledDNDEnd').bind()}
597 type="time" 602 type="time"
598 /> 603 />
599 </div> 604 </div>
@@ -711,7 +716,7 @@ class EditSettingsForm extends Component {
711 max={SPLIT_COLUMNS_MAX} 716 max={SPLIT_COLUMNS_MAX}
712 placeholder={`${SPLIT_COLUMNS_MIN}-${SPLIT_COLUMNS_MAX}`} 717 placeholder={`${SPLIT_COLUMNS_MIN}-${SPLIT_COLUMNS_MAX}`}
713 onChange={e => this.submit(e)} 718 onChange={e => this.submit(e)}
714 field={form.$('splitColumns')} 719 {...form.$('splitColumns').bind()}
715 /> 720 />
716 )} 721 )}
717 722
@@ -825,7 +830,7 @@ class EditSettingsForm extends Component {
825 <Input 830 <Input
826 placeholder={intl.formatMessage(messages.lockedPassword)} 831 placeholder={intl.formatMessage(messages.lockedPassword)}
827 onChange={e => this.submit(e)} 832 onChange={e => this.submit(e)}
828 field={form.$('lockedPassword')} 833 {...form.$('lockedPassword')}
829 type="password" 834 type="password"
830 scorePassword 835 scorePassword
831 showPasswordToggle 836 showPasswordToggle
@@ -835,7 +840,7 @@ class EditSettingsForm extends Component {
835 <Input 840 <Input
836 placeholder="Lock after inactivity" 841 placeholder="Lock after inactivity"
837 onChange={e => this.submit(e)} 842 onChange={e => this.submit(e)}
838 field={form.$('inactivityLock')} 843 {...form.$('inactivityLock')}
839 autoFocus 844 autoFocus
840 /> 845 />
841 <p>{intl.formatMessage(messages.inactivityLockInfo)}</p> 846 <p>{intl.formatMessage(messages.inactivityLockInfo)}</p>
@@ -927,7 +932,7 @@ class EditSettingsForm extends Component {
927 <Input 932 <Input
928 placeholder="User Agent" 933 placeholder="User Agent"
929 onChange={e => this.submit(e)} 934 onChange={e => this.submit(e)}
930 field={form.$('userAgentPref')} 935 {...form.$('userAgentPref').bind()}
931 /> 936 />
932 <p className="settings__help"> 937 <p className="settings__help">
933 {intl.formatMessage(globalMessages.userAgentHelp)} 938 {intl.formatMessage(globalMessages.userAgentHelp)}
@@ -1128,4 +1133,4 @@ class EditSettingsForm extends Component {
1128 } 1133 }
1129} 1134}
1130 1135
1131export default injectIntl(observer(EditSettingsForm)); 1136export default injectIntl(EditSettingsForm);
diff --git a/src/components/settings/user/EditUserForm.js b/src/components/settings/user/EditUserForm.tsx
index c2773a47d..3b604a79f 100644
--- a/src/components/settings/user/EditUserForm.js
+++ b/src/components/settings/user/EditUserForm.tsx
@@ -1,9 +1,8 @@
1import { Component } from 'react'; 1import { Component, FormEvent, FormEventHandler, ReactElement } from 'react';
2import PropTypes from 'prop-types'; 2import { observer } from 'mobx-react';
3import { observer, PropTypes as MobxPropTypes } from 'mobx-react'; 3import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
4import { defineMessages, injectIntl } from 'react-intl';
5import { Link } from 'react-router-dom'; 4import { Link } from 'react-router-dom';
6 5import { noop } from 'lodash';
7import Input from '../../ui/input/index'; 6import Input from '../../ui/input/index';
8import Form from '../../../lib/Form'; 7import Form from '../../../lib/Form';
9import Button from '../../ui/button'; 8import Button from '../../ui/button';
@@ -38,15 +37,16 @@ const messages = defineMessages({
38 }, 37 },
39}); 38});
40 39
41class EditUserForm extends Component { 40interface IProps extends WrappedComponentProps {
42 static propTypes = { 41 status: string[];
43 status: MobxPropTypes.observableArray.isRequired, 42 form: Form;
44 form: PropTypes.instanceOf(Form).isRequired, 43 onSubmit: FormEventHandler<HTMLFormElement>;
45 onSubmit: PropTypes.func.isRequired, 44 isSaving: boolean;
46 isSaving: PropTypes.bool.isRequired, 45}
47 };
48 46
49 submit(e) { 47@observer
48class EditUserForm extends Component<IProps> {
49 submit(e: FormEvent<HTMLFormElement>): void {
50 e.preventDefault(); 50 e.preventDefault();
51 this.props.form.submit({ 51 this.props.form.submit({
52 onSuccess: form => { 52 onSuccess: form => {
@@ -57,14 +57,14 @@ class EditUserForm extends Component {
57 }); 57 });
58 } 58 }
59 59
60 render() { 60 render(): ReactElement {
61 const { 61 const {
62 // user, 62 // user,
63 status, 63 status,
64 form, 64 form,
65 isSaving, 65 isSaving,
66 intl,
66 } = this.props; 67 } = this.props;
67 const { intl } = this.props;
68 68
69 return ( 69 return (
70 <div className="settings__main"> 70 <div className="settings__main">
@@ -92,9 +92,9 @@ class EditUserForm extends Component {
92 <Input {...form.$('lastname').bind()} /> 92 <Input {...form.$('lastname').bind()} />
93 </div> 93 </div>
94 <Input {...form.$('email').bind()} /> 94 <Input {...form.$('email').bind()} />
95 <Radio field={form.$('accountType')} /> 95 <Radio field={form.$('accountType')} className="" />
96 {form.$('accountType').value === 'company' && ( 96 {form.$('accountType').value === 'company' && (
97 <Input field={form.$('organization')} /> 97 <Input {...form.$('organization').bind()} />
98 )} 98 )}
99 <H2>{intl.formatMessage(messages.headlinePassword)}</H2> 99 <H2>{intl.formatMessage(messages.headlinePassword)}</H2>
100 <Input {...form.$('oldPassword').bind()} showPasswordToggle /> 100 <Input {...form.$('oldPassword').bind()} showPasswordToggle />
@@ -114,12 +114,14 @@ class EditUserForm extends Component {
114 loaded={!isSaving} 114 loaded={!isSaving}
115 buttonType="secondary" 115 buttonType="secondary"
116 disabled 116 disabled
117 onClick={noop}
117 /> 118 />
118 ) : ( 119 ) : (
119 <Button 120 <Button
120 type="submit" 121 type="submit"
121 label={intl.formatMessage(messages.buttonSave)} 122 label={intl.formatMessage(messages.buttonSave)}
122 htmlForm="form" 123 htmlForm="form"
124 onClick={noop}
123 /> 125 />
124 )} 126 )}
125 </div> 127 </div>
@@ -128,4 +130,4 @@ class EditUserForm extends Component {
128 } 130 }
129} 131}
130 132
131export default injectIntl(observer(EditUserForm)); 133export default injectIntl(EditUserForm);
diff --git a/src/components/ui/ColorPickerInput.tsx b/src/components/ui/ColorPickerInput.tsx
index 7e3965331..da1fffb71 100644
--- a/src/components/ui/ColorPickerInput.tsx
+++ b/src/components/ui/ColorPickerInput.tsx
@@ -1,15 +1,25 @@
1import { ChangeEvent, Component, createRef, RefObject } from 'react'; 1import {
2 ChangeEvent,
3 ChangeEventHandler,
4 Component,
5 createRef,
6 RefObject,
7} from 'react';
2import { observer } from 'mobx-react'; 8import { observer } from 'mobx-react';
3import classnames from 'classnames'; 9import classnames from 'classnames';
4import { SliderPicker } from 'react-color'; 10import { SliderPicker } from 'react-color';
11import { noop } from 'lodash';
5import { Field } from '../../@types/mobx-form.types'; 12import { Field } from '../../@types/mobx-form.types';
6 13
7interface IProps { 14interface IProps {
8 field: Field; 15 field: Field;
9 className?: string; 16 className?: string;
10 focus?: boolean; 17 focus?: boolean;
18 onChange: ChangeEventHandler<HTMLInputElement>;
11} 19}
12 20
21// TODO - [TS DEBT] check if field can be spread instead of having it single field attribute in interface
22@observer
13class ColorPickerInput extends Component<IProps> { 23class ColorPickerInput extends Component<IProps> {
14 private inputElement: RefObject<HTMLInputElement> = 24 private inputElement: RefObject<HTMLInputElement> =
15 createRef<HTMLInputElement>(); 25 createRef<HTMLInputElement>();
@@ -22,7 +32,9 @@ class ColorPickerInput extends Component<IProps> {
22 } 32 }
23 33
24 onChange(e: ChangeEvent<HTMLInputElement>) { 34 onChange(e: ChangeEvent<HTMLInputElement>) {
25 const { field } = this.props; 35 const { field, onChange = noop } = this.props;
36
37 onChange(e);
26 if (field.onChange) { 38 if (field.onChange) {
27 field.onChange(e); 39 field.onChange(e);
28 } 40 }
@@ -87,4 +99,4 @@ class ColorPickerInput extends Component<IProps> {
87 } 99 }
88} 100}
89 101
90export default observer(ColorPickerInput); 102export default ColorPickerInput;
diff --git a/src/components/ui/Input.tsx b/src/components/ui/Input.tsx
deleted file mode 100644
index c22dc5838..000000000
--- a/src/components/ui/Input.tsx
+++ /dev/null
@@ -1,170 +0,0 @@
1import {
2 ChangeEvent,
3 ChangeEventHandler,
4 Component,
5 createRef,
6 ReactElement,
7 RefObject,
8} from 'react';
9import { observer } from 'mobx-react';
10import classnames from 'classnames';
11import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
12import { mdiEye, mdiEyeOff } from '@mdi/js';
13import { noop } from 'lodash';
14import { scorePassword as scorePasswordFunc } from '../../helpers/password-helpers';
15import Icon from './icon';
16import { Field } from '../../@types/mobx-form.types';
17
18const messages = defineMessages({
19 passwordToggle: {
20 id: 'settings.app.form.passwordToggle',
21 defaultMessage: 'Password toggle',
22 },
23});
24
25interface IProps extends WrappedComponentProps {
26 field: Field;
27 className?: string;
28 focus?: boolean;
29 showPasswordToggle?: boolean;
30 showLabel?: boolean;
31 scorePassword?: boolean;
32 prefix?: string;
33 suffix?: string;
34 placeholder?: string;
35 onChange?: ChangeEventHandler<HTMLInputElement>;
36}
37
38interface IState {
39 showPassword: boolean;
40 passwordScore: number;
41}
42
43// Can this file be merged into the './input/index.tsx' file?
44@observer
45class Input extends Component<IProps, IState> {
46 private inputElement: RefObject<HTMLInputElement> =
47 createRef<HTMLInputElement>();
48
49 constructor(props: IProps) {
50 super(props);
51
52 this.state = {
53 showPassword: false,
54 passwordScore: 0,
55 };
56 }
57
58 componentDidMount(): void {
59 const { focus = false } = this.props;
60 if (focus) {
61 this.focus();
62 }
63 }
64
65 onChange(e: ChangeEvent<HTMLInputElement>): void {
66 const { field, scorePassword, onChange = noop } = this.props;
67
68 if (field.onChange) {
69 onChange(e);
70 field.onChange(e);
71 }
72
73 if (scorePassword) {
74 this.setState({
75 passwordScore: scorePasswordFunc(field.value as string),
76 });
77 }
78 }
79
80 focus() {
81 if (this.inputElement && this.inputElement.current) {
82 this.inputElement.current!.focus();
83 }
84 }
85
86 render(): ReactElement {
87 const {
88 field,
89 className = null,
90 showPasswordToggle = false,
91 showLabel = true,
92 scorePassword = false,
93 prefix = '',
94 suffix = '',
95 intl,
96 } = this.props;
97
98 const { passwordScore } = this.state;
99
100 let { type } = field;
101 if (type === 'password' && this.state.showPassword) {
102 type = 'text';
103 }
104
105 return (
106 <div
107 className={classnames({
108 'franz-form__field': true,
109 'has-error': field.error,
110 [`${className}`]: className,
111 })}
112 >
113 <div className="franz-form__input-wrapper">
114 {prefix && <span className="franz-form__input-prefix">{prefix}</span>}
115 <input
116 id={field.id}
117 type={type}
118 className="franz-form__input"
119 name={field.name}
120 value={field.value}
121 placeholder={field.placeholder}
122 onChange={e => this.onChange(e)}
123 onBlur={field.onBlur}
124 onFocus={field.onFocus}
125 ref={this.inputElement}
126 disabled={field.disabled}
127 />
128 {suffix && <span className="franz-form__input-suffix">{suffix}</span>}
129 {showPasswordToggle && (
130 <button
131 type="button"
132 className={classnames({
133 'franz-form__input-modifier': true,
134 })}
135 onClick={() =>
136 this.setState(prevState => ({
137 showPassword: !prevState.showPassword,
138 }))
139 }
140 tabIndex={-1}
141 aria-label={intl.formatMessage(messages.passwordToggle)}
142 >
143 <Icon icon={this.state.showPassword ? mdiEye : mdiEyeOff} />
144 </button>
145 )}
146 {scorePassword && (
147 <div className="franz-form__password-score">
148 {/* <progress value={this.state.passwordScore} max="100" /> */}
149 <meter
150 value={passwordScore < 5 ? 5 : passwordScore}
151 low={30}
152 high={75}
153 optimum={100}
154 max={100}
155 />
156 </div>
157 )}
158 </div>
159 {field.label && showLabel && (
160 <label className="franz-form__label" htmlFor={field.name}>
161 {field.label}
162 </label>
163 )}
164 {field.error && <div className="franz-form__error">{field.error}</div>}
165 </div>
166 );
167 }
168}
169
170export default injectIntl(Input);
diff --git a/src/components/ui/input/index.tsx b/src/components/ui/input/index.tsx
index 2a36d7aa9..cb26c0ea4 100644
--- a/src/components/ui/input/index.tsx
+++ b/src/components/ui/input/index.tsx
@@ -1,5 +1,4 @@
1import { mdiEye, mdiEyeOff } from '@mdi/js'; 1import { mdiEye, mdiEyeOff } from '@mdi/js';
2import Icon from '@mdi/react';
3import classnames from 'classnames'; 2import classnames from 'classnames';
4import { 3import {
5 Component, 4 Component,
@@ -7,9 +6,13 @@ import {
7 InputHTMLAttributes, 6 InputHTMLAttributes,
8 ReactElement, 7 ReactElement,
9 RefObject, 8 RefObject,
9 KeyboardEvent,
10} from 'react'; 10} from 'react';
11import injectSheet, { WithStylesProps } from 'react-jss'; 11import withStyles, { WithStylesProps } from 'react-jss';
12import { noop } from 'lodash'; 12import { noop } from 'lodash';
13import { observer } from 'mobx-react';
14import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
15import Icon from '../icon';
13import { IFormField } from '../typings/generic'; 16import { IFormField } from '../typings/generic';
14import Error from '../error'; 17import Error from '../error';
15import Label from '../label'; 18import Label from '../label';
@@ -17,6 +20,12 @@ import Wrapper from '../wrapper';
17import { scorePasswordFunc } from './scorePassword'; 20import { scorePasswordFunc } from './scorePassword';
18import styles from './styles'; 21import styles from './styles';
19 22
23const messages = defineMessages({
24 passwordToggle: {
25 id: 'settings.app.form.passwordToggle',
26 defaultMessage: 'Password toggle',
27 },
28});
20interface IData { 29interface IData {
21 [index: string]: string; 30 [index: string]: string;
22} 31}
@@ -24,7 +33,8 @@ interface IData {
24interface IProps 33interface IProps
25 extends InputHTMLAttributes<HTMLInputElement>, 34 extends InputHTMLAttributes<HTMLInputElement>,
26 IFormField, 35 IFormField,
27 WithStylesProps<typeof styles> { 36 WithStylesProps<typeof styles>,
37 WrappedComponentProps {
28 focus?: boolean; 38 focus?: boolean;
29 prefix?: string; 39 prefix?: string;
30 suffix?: string; 40 suffix?: string;
@@ -40,8 +50,10 @@ interface IState {
40 passwordScore: number; 50 passwordScore: number;
41} 51}
42 52
43class InputComponent extends Component<IProps, IState> { 53@observer
44 private inputRef: RefObject<HTMLInputElement> = createRef<HTMLInputElement>(); 54class Input extends Component<IProps, IState> {
55 private inputElement: RefObject<HTMLInputElement> =
56 createRef<HTMLInputElement>();
45 57
46 constructor(props: IProps) { 58 constructor(props: IProps) {
47 super(props); 59 super(props);
@@ -53,36 +65,40 @@ class InputComponent extends Component<IProps, IState> {
53 } 65 }
54 66
55 componentDidMount(): void { 67 componentDidMount(): void {
56 const { focus, data = {} } = this.props; 68 const { focus = false, data = {} } = this.props;
57 69
58 if (this.inputRef && this.inputRef.current) { 70 if (this.inputElement && this.inputElement.current) {
59 if (focus) { 71 if (focus) {
60 this.inputRef.current.focus(); 72 this.inputElement.current.focus();
61 } 73 }
62 74
63 for (const key of Object.keys(data)) 75 for (const key of Object.keys(data)) {
64 this.inputRef.current!.dataset[key] = data[key]; 76 this.inputElement.current.dataset[key] = data[key];
77 }
65 } 78 }
66 } 79 }
67 80
68 onChange(e: React.ChangeEvent<HTMLInputElement>): void { 81 onChange(e: React.ChangeEvent<HTMLInputElement>): void {
69 const { scorePassword, onChange } = this.props; 82 const { scorePassword, onChange = noop } = this.props;
70 83
71 if (onChange) { 84 onChange(e);
72 onChange(e);
73 }
74 85
75 if (this.inputRef && this.inputRef.current && scorePassword) { 86 if (scorePassword) {
87 console.log(
88 '--->',
89 e.target.value,
90 scorePasswordFunc(e.target.value as string),
91 );
76 this.setState({ 92 this.setState({
77 passwordScore: scorePasswordFunc(this.inputRef.current.value), 93 passwordScore: scorePasswordFunc(e.target.value),
78 }); 94 });
79 } 95 }
80 } 96 }
81 97
82 onInputKeyPress(e: React.KeyboardEvent): void { 98 onInputKeyPress(e: KeyboardEvent<HTMLInputElement>): void {
83 if (e.key === 'Enter') { 99 if (e.key === 'Enter') {
84 const { onEnterKey } = this.props; 100 const { onEnterKey = noop } = this.props;
85 onEnterKey && onEnterKey(); 101 onEnterKey();
86 } 102 }
87 } 103 }
88 104
@@ -113,10 +129,9 @@ class InputComponent extends Component<IProps, IState> {
113 type = 'text', 129 type = 'text',
114 disabled = false, 130 disabled = false,
115 readOnly, 131 readOnly,
132 intl,
116 } = this.props; 133 } = this.props;
117
118 const { showPassword, passwordScore } = this.state; 134 const { showPassword, passwordScore } = this.state;
119
120 const inputType = type === 'password' && showPassword ? 'text' : type; 135 const inputType = type === 'password' && showPassword ? 'text' : type;
121 136
122 return ( 137 return (
@@ -125,79 +140,85 @@ class InputComponent extends Component<IProps, IState> {
125 identifier="franz-input" 140 identifier="franz-input"
126 noMargin={noMargin} 141 noMargin={noMargin}
127 > 142 >
128 <Label 143 {label && showLabel && (
129 title={label} 144 <Label
130 showLabel={showLabel} 145 title={label}
131 htmlFor={id} 146 showLabel={showLabel}
132 className={classes.label} 147 htmlFor={id}
133 isRequired={required} 148 className={classes.label}
149 isRequired={required}
150 />
151 )}
152 <div
153 className={classnames({
154 [`${inputClassName}`]: inputClassName,
155 // [`${classes.hasPasswordScore}`]: scorePassword,
156 [`${classes.wrapper}`]: true,
157 [`${classes.disabled}`]: disabled,
158 [`${classes.hasError}`]: error,
159 })}
134 > 160 >
161 {prefix && <span className={classes.prefix}>{prefix}</span>}
162 <input
163 id={id}
164 type={inputType}
165 name={name}
166 value={value as string}
167 placeholder={placeholder}
168 spellCheck={spellCheck}
169 className={classes.input}
170 ref={this.inputElement}
171 onChange={this.onChange.bind(this)}
172 onFocus={onFocus}
173 onBlur={onBlur}
174 disabled={disabled}
175 onKeyPress={this.onInputKeyPress.bind(this)}
176 min={min}
177 max={max}
178 step={step}
179 readOnly={readOnly}
180 />
181
182 {suffix && <span className={classes.suffix}>{suffix}</span>}
183
184 {showPasswordToggle && (
185 <button
186 type="button"
187 className={classnames({
188 'franz-form__input-modifier': true,
189 })}
190 onClick={() =>
191 this.setState(prevState => ({
192 showPassword: !prevState.showPassword,
193 }))
194 }
195 tabIndex={-1}
196 aria-label={intl.formatMessage(messages.passwordToggle)}
197 >
198 <Icon icon={this.state.showPassword ? mdiEye : mdiEyeOff} />
199 </button>
200 )}
201 </div>
202 {scorePassword && (
135 <div 203 <div
136 className={classnames({ 204 className={classnames({
137 [`${inputClassName}`]: inputClassName, 205 [`${classes.passwordScore}`]: true,
138 [`${classes.hasPasswordScore}`]: scorePassword,
139 [`${classes.wrapper}`]: true,
140 [`${classes.disabled}`]: disabled,
141 [`${classes.hasError}`]: error, 206 [`${classes.hasError}`]: error,
142 })} 207 })}
143 > 208 >
144 {prefix && <span className={classes.prefix}>{prefix}</span>} 209 <meter
145 <input 210 value={passwordScore < 5 ? 5 : passwordScore}
146 id={id} 211 low={30}
147 type={inputType} 212 high={75}
148 name={name} 213 optimum={100}
149 value={value as string} 214 max={100}
150 placeholder={placeholder}
151 spellCheck={spellCheck}
152 className={classes.input}
153 ref={this.inputRef}
154 onChange={this.onChange.bind(this)}
155 onFocus={onFocus}
156 onBlur={onBlur}
157 disabled={disabled}
158 onKeyPress={this.onInputKeyPress.bind(this)}
159 min={min}
160 max={max}
161 step={step}
162 readOnly={readOnly}
163 /> 215 />
164 {suffix && <span className={classes.suffix}>{suffix}</span>}
165 {showPasswordToggle && (
166 <button
167 type="button"
168 className={classes.formModifier}
169 onClick={() =>
170 this.setState(prevState => ({
171 showPassword: !prevState.showPassword,
172 }))
173 }
174 tabIndex={-1}
175 >
176 <Icon path={!showPassword ? mdiEye : mdiEyeOff} />
177 </button>
178 )}
179 </div> 216 </div>
180 {scorePassword && ( 217 )}
181 <div
182 className={classnames({
183 [`${classes.passwordScore}`]: true,
184 [`${classes.hasError}`]: error,
185 })}
186 >
187 <meter
188 value={passwordScore < 5 ? 5 : passwordScore}
189 low={30}
190 high={75}
191 optimum={100}
192 max={100}
193 />
194 </div>
195 )}
196 </Label>
197 {error && <Error message={error} />} 218 {error && <Error message={error} />}
198 </Wrapper> 219 </Wrapper>
199 ); 220 );
200 } 221 }
201} 222}
202 223
203export default injectSheet(styles, { injectTheme: true })(InputComponent); 224export default injectIntl(withStyles(styles, { injectTheme: true })(Input));
diff --git a/src/components/ui/input/styles.ts b/src/components/ui/input/styles.ts
index 04c1b3991..ebae0e40d 100644
--- a/src/components/ui/input/styles.ts
+++ b/src/components/ui/input/styles.ts
@@ -49,7 +49,6 @@ export default (theme: Theme) => ({
49 }, 49 },
50 passwordScore: { 50 passwordScore: {
51 background: theme.inputScorePasswordBackground, 51 background: theme.inputScorePasswordBackground,
52 border: theme.inputBorder,
53 borderTopWidth: 0, 52 borderTopWidth: 0,
54 borderBottomLeftRadius: theme.borderRadiusSmall, 53 borderBottomLeftRadius: theme.borderRadiusSmall,
55 borderBottomRightRadius: theme.borderRadiusSmall, 54 borderBottomRightRadius: theme.borderRadiusSmall,
diff --git a/src/containers/auth/LockedScreen.tsx b/src/containers/auth/LockedScreen.tsx
index 611a0757c..a4cb43f73 100644
--- a/src/containers/auth/LockedScreen.tsx
+++ b/src/containers/auth/LockedScreen.tsx
@@ -12,16 +12,19 @@ interface IProps {
12 stores?: RealStores; 12 stores?: RealStores;
13} 13}
14 14
15interface IState {
16 error: boolean;
17}
18
15@inject('stores', 'actions') 19@inject('stores', 'actions')
16@observer 20@observer
17class LockedScreen extends Component<IProps> { 21class LockedScreen extends Component<IProps, IState> {
18 state = {
19 error: false,
20 };
21
22 constructor(props: StoresProps) { 22 constructor(props: StoresProps) {
23 super(props); 23 super(props);
24 24
25 this.state = {
26 error: false,
27 };
25 this.onSubmit = this.onSubmit.bind(this); 28 this.onSubmit = this.onSubmit.bind(this);
26 this.unlock = this.unlock.bind(this); 29 this.unlock = this.unlock.bind(this);
27 } 30 }
@@ -43,9 +46,7 @@ class LockedScreen extends Component<IProps> {
43 }); 46 });
44 } else { 47 } else {
45 this.setState({ 48 this.setState({
46 error: { 49 error: true,
47 code: 'invalid-credentials',
48 },
49 }); 50 });
50 } 51 }
51 } 52 }
@@ -71,7 +72,7 @@ class LockedScreen extends Component<IProps> {
71 unlock={this.unlock} 72 unlock={this.unlock}
72 useTouchIdToUnlock={useTouchIdToUnlock} 73 useTouchIdToUnlock={useTouchIdToUnlock}
73 isSubmitting={stores!.user.loginRequest.isExecuting} 74 isSubmitting={stores!.user.loginRequest.isExecuting}
74 error={this.state.error || {}} 75 error={this.state.error}
75 /> 76 />
76 </div> 77 </div>
77 </div> 78 </div>
diff --git a/src/containers/settings/EditSettingsScreen.tsx b/src/containers/settings/EditSettingsScreen.tsx
index 162d422ce..0d8a0a758 100644
--- a/src/containers/settings/EditSettingsScreen.tsx
+++ b/src/containers/settings/EditSettingsScreen.tsx
@@ -1,7 +1,7 @@
1import { ipcRenderer } from 'electron'; 1import { ipcRenderer } from 'electron';
2import { Component, ReactElement } from 'react'; 2import { Component, ReactElement } from 'react';
3import { inject, observer } from 'mobx-react'; 3import { inject, observer } from 'mobx-react';
4import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
5 5
6import { FormFields } from '../../@types/mobx-form.types'; 6import { FormFields } from '../../@types/mobx-form.types';
7import { StoresProps } from '../../@types/ferdium-components.types'; 7import { StoresProps } from '../../@types/ferdium-components.types';
@@ -314,10 +314,10 @@ const messages = defineMessages({
314 }, 314 },
315}); 315});
316 316
317interface EditSettingsScreenProps extends StoresProps { 317interface EditSettingsScreenProps extends StoresProps, WrappedComponentProps {}
318 intl: any;
319}
320 318
319@inject('stores', 'actions')
320@observer
321class EditSettingsScreen extends Component<EditSettingsScreenProps> { 321class EditSettingsScreen extends Component<EditSettingsScreenProps> {
322 state = { 322 state = {
323 lockedPassword: '', 323 lockedPassword: '',
@@ -962,9 +962,6 @@ class EditSettingsScreen extends Component<EditSettingsScreenProps> {
962 } 962 }
963 isSplitModeEnabled={this.props.stores.settings.app.splitMode} 963 isSplitModeEnabled={this.props.stores.settings.app.splitMode}
964 isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser} 964 isTodosActivated={this.props.stores.todos.isFeatureEnabledByUser}
965 isUsingCustomTodoService={
966 this.props.stores.todos.isUsingCustomTodoService
967 }
968 openProcessManager={() => this.openProcessManager()} 965 openProcessManager={() => this.openProcessManager()}
969 isOnline={app.isOnline} 966 isOnline={app.isOnline}
970 serverURL={importExportURL()} 967 serverURL={importExportURL()}
@@ -974,6 +971,4 @@ class EditSettingsScreen extends Component<EditSettingsScreenProps> {
974 } 971 }
975} 972}
976 973
977export default injectIntl( 974export default injectIntl(EditSettingsScreen);
978 inject('stores', 'actions')(observer(EditSettingsScreen)),
979);
diff --git a/src/features/basicAuth/Component.js b/src/features/basicAuth/Component.tsx
index acba5a90d..e20f7641b 100644
--- a/src/features/basicAuth/Component.js
+++ b/src/features/basicAuth/Component.tsx
@@ -1,17 +1,14 @@
1import { Component } from 'react'; 1import { Component, FormEvent, ReactElement } from 'react';
2import PropTypes from 'prop-types'; 2import injectSheet, { WithStylesProps } from 'react-jss';
3import injectSheet from 'react-jss';
4import { observer } from 'mobx-react'; 3import { observer } from 'mobx-react';
5import { defineMessages, injectIntl } from 'react-intl'; 4import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl';
6import classnames from 'classnames'; 5import classnames from 'classnames';
7 6import { noop } from 'lodash';
8import Modal from '../../components/ui/Modal'; 7import Modal from '../../components/ui/Modal';
9import Input from '../../components/ui/Input'; 8import Input from '../../components/ui/input/index';
10import Button from '../../components/ui/button'; 9import Button from '../../components/ui/button';
11
12import { state, resetState, sendCredentials, cancelLogin } from './store'; 10import { state, resetState, sendCredentials, cancelLogin } from './store';
13import Form from './Form'; 11import Form from './Form';
14
15import styles from './styles'; 12import styles from './styles';
16import globalMessages from '../../i18n/globalMessages'; 13import globalMessages from '../../i18n/globalMessages';
17import { H1 } from '../../components/ui/headline'; 14import { H1 } from '../../components/ui/headline';
@@ -23,33 +20,31 @@ const messages = defineMessages({
23 }, 20 },
24}); 21});
25 22
26class BasicAuthModal extends Component { 23interface IProps
27 static propTypes = { 24 extends WithStylesProps<typeof styles>,
28 classes: PropTypes.object.isRequired, 25 WrappedComponentProps {}
29 };
30 26
31 submit(e) { 27@observer
28class BasicAuthModal extends Component<IProps> {
29 submit(e: FormEvent<HTMLFormElement>): void {
32 e.preventDefault(); 30 e.preventDefault();
33
34 const values = Form.values(); 31 const values = Form.values();
35
36 sendCredentials(values.user, values.password); 32 sendCredentials(values.user, values.password);
37 resetState(); 33 resetState();
38 } 34 }
39 35
40 cancel() { 36 cancel(): void {
41 cancelLogin(); 37 cancelLogin();
42 this.close(); 38 this.close();
43 } 39 }
44 40
45 close() { 41 close(): void {
46 resetState(); 42 resetState();
47 state.isModalVisible = false; 43 state.isModalVisible = false;
48 } 44 }
49 45
50 render() { 46 render(): ReactElement | null {
51 const { classes } = this.props; 47 const { classes } = this.props;
52
53 const { isModalVisible, authInfo } = state; 48 const { isModalVisible, authInfo } = state;
54 49
55 if (!authInfo) { 50 if (!authInfo) {
@@ -76,9 +71,9 @@ class BasicAuthModal extends Component {
76 onSubmit={this.submit.bind(this)} 71 onSubmit={this.submit.bind(this)}
77 className={classnames('franz-form', classes.form)} 72 className={classnames('franz-form', classes.form)}
78 > 73 >
79 <Input field={Form.$('user')} showLabel={false} /> 74 <Input {...Form.$('user').bind()} showLabel={false} />
80 <Input 75 <Input
81 field={Form.$('password')} 76 {...Form.$('password').bind()}
82 showLabel={false} 77 showLabel={false}
83 showPasswordToggle 78 showPasswordToggle
84 /> 79 />
@@ -89,7 +84,11 @@ class BasicAuthModal extends Component {
89 buttonType="secondary" 84 buttonType="secondary"
90 onClick={this.cancel.bind(this)} 85 onClick={this.cancel.bind(this)}
91 /> 86 />
92 <Button type="submit" label={intl.formatMessage(messages.signIn)} /> 87 <Button
88 type="submit"
89 label={intl.formatMessage(messages.signIn)}
90 onClick={noop}
91 />
93 </div> 92 </div>
94 </form> 93 </form>
95 </Modal> 94 </Modal>
@@ -97,5 +96,5 @@ class BasicAuthModal extends Component {
97 } 96 }
98} 97}
99export default injectIntl( 98export default injectIntl(
100 injectSheet(styles, { injectTheme: true })(observer(BasicAuthModal)), 99 injectSheet(styles, { injectTheme: true })(BasicAuthModal),
101); 100);
diff --git a/src/features/basicAuth/store.ts b/src/features/basicAuth/store.ts
index e0ae8ba17..0fc289916 100644
--- a/src/features/basicAuth/store.ts
+++ b/src/features/basicAuth/store.ts
@@ -3,7 +3,17 @@ import { ipcRenderer } from 'electron';
3 3
4const debug = require('../../preload-safe-debug')('Ferdium:feature:basicAuth'); 4const debug = require('../../preload-safe-debug')('Ferdium:feature:basicAuth');
5 5
6const defaultState = { 6interface IAuthInfo {
7 host: string;
8 port: number;
9}
10interface IDefaultState {
11 isModalVisible: boolean;
12 service: null;
13 authInfo: IAuthInfo | null;
14}
15
16const defaultState: IDefaultState = {
7 isModalVisible: true, 17 isModalVisible: true,
8 service: null, 18 service: null,
9 authInfo: null, 19 authInfo: null,
diff --git a/src/features/publishDebugInfo/Component.tsx b/src/features/publishDebugInfo/Component.tsx
index e265902dd..3c6729ba0 100644
--- a/src/features/publishDebugInfo/Component.tsx
+++ b/src/features/publishDebugInfo/Component.tsx
@@ -163,7 +163,6 @@ class PublishDebugLogModal extends Component<IProps, IState> {
163 <p className={classes.info}> 163 <p className={classes.info}>
164 {intl.formatMessage(messages.published)} 164 {intl.formatMessage(messages.published)}
165 </p> 165 </p>
166 {/* TODO - [TS DEBT] need to check if <Input/> take readOnly and use it */}
167 <Input showLabel={false} value={`${DEBUG_API}/${log}`} readOnly /> 166 <Input showLabel={false} value={`${DEBUG_API}/${log}`} readOnly />
168 </> 167 </>
169 )} 168 )}
diff --git a/src/i18n/locales/en-US.json b/src/i18n/locales/en-US.json
index 63face219..c34106044 100644
--- a/src/i18n/locales/en-US.json
+++ b/src/i18n/locales/en-US.json
@@ -62,7 +62,7 @@
62 "locked.password.label": "Password", 62 "locked.password.label": "Password",
63 "locked.submit.label": "Unlock", 63 "locked.submit.label": "Unlock",
64 "locked.touchId": "Unlock with Touch ID", 64 "locked.touchId": "Unlock with Touch ID",
65 "locked.touchIdPrompt": "unlock via Touch ID", 65 "locked.touchIdPrompt": "Unlock with Touch ID",
66 "locked.unlockWithPassword": "Unlock with Password", 66 "locked.unlockWithPassword": "Unlock with Password",
67 "login.changeServer": "Change here!", 67 "login.changeServer": "Change here!",
68 "login.changeServerMessage": "You are using {serverNameParse} Server, do you want to switch?", 68 "login.changeServerMessage": "You are using {serverNameParse} Server, do you want to switch?",
@@ -448,6 +448,7 @@
448 "sidebar.openWorkspaceDrawer": "Open workspace drawer", 448 "sidebar.openWorkspaceDrawer": "Open workspace drawer",
449 "sidebar.splitModeToggle": "Split Mode Toggle", 449 "sidebar.splitModeToggle": "Split Mode Toggle",
450 "sidebar.unmuteApp": "Enable notifications & audio", 450 "sidebar.unmuteApp": "Enable notifications & audio",
451 "signup.company.label": "Company",
451 "signup.email.label": "Email address", 452 "signup.email.label": "Email address",
452 "signup.emailDuplicate": "A user with that email address already exists", 453 "signup.emailDuplicate": "A user with that email address already exists",
453 "signup.firstname.label": "First Name", 454 "signup.firstname.label": "First Name",