diff options
Diffstat (limited to 'src/components/auth/Login.tsx')
-rw-r--r-- | src/components/auth/Login.tsx | 210 |
1 files changed, 210 insertions, 0 deletions
diff --git a/src/components/auth/Login.tsx b/src/components/auth/Login.tsx new file mode 100644 index 000000000..65381fe04 --- /dev/null +++ b/src/components/auth/Login.tsx | |||
@@ -0,0 +1,210 @@ | |||
1 | import { Component, FormEvent, ReactElement } from 'react'; | ||
2 | import { observer, inject } from 'mobx-react'; | ||
3 | import { defineMessages, injectIntl, WrappedComponentProps } from 'react-intl'; | ||
4 | import { mdiArrowLeftCircle } from '@mdi/js'; | ||
5 | import { noop } from 'lodash'; | ||
6 | import Icon from '../ui/icon'; | ||
7 | import { LIVE_FRANZ_API } from '../../config'; | ||
8 | import { API_VERSION } from '../../environment-remote'; | ||
9 | import { serverBase } from '../../api/apiBase'; // TODO: Remove this line after fixing password recovery in-app | ||
10 | import Form from '../../lib/Form'; | ||
11 | import { required, email } from '../../helpers/validation-helpers'; | ||
12 | import Input from '../ui/Input'; | ||
13 | import Button from '../ui/button'; | ||
14 | import Link from '../ui/Link'; | ||
15 | import { H1 } from '../ui/headline'; | ||
16 | import { | ||
17 | GlobalError, | ||
18 | StoresProps, | ||
19 | } from '../../@types/ferdium-components.types'; | ||
20 | |||
21 | const messages = defineMessages({ | ||
22 | headline: { | ||
23 | id: 'login.headline', | ||
24 | defaultMessage: 'Sign in', | ||
25 | }, | ||
26 | emailLabel: { | ||
27 | id: 'login.email.label', | ||
28 | defaultMessage: 'Email address', | ||
29 | }, | ||
30 | passwordLabel: { | ||
31 | id: 'login.password.label', | ||
32 | defaultMessage: 'Password', | ||
33 | }, | ||
34 | submitButtonLabel: { | ||
35 | id: 'login.submit.label', | ||
36 | defaultMessage: 'Sign in', | ||
37 | }, | ||
38 | invalidCredentials: { | ||
39 | id: 'login.invalidCredentials', | ||
40 | defaultMessage: 'Email or password not valid', | ||
41 | }, | ||
42 | customServerQuestion: { | ||
43 | id: 'login.customServerQuestion', | ||
44 | defaultMessage: 'Using a custom Ferdium server?', | ||
45 | }, | ||
46 | customServerSuggestion: { | ||
47 | id: 'login.customServerSuggestion', | ||
48 | defaultMessage: 'Try importing your Franz account', | ||
49 | }, | ||
50 | tokenExpired: { | ||
51 | id: 'login.tokenExpired', | ||
52 | defaultMessage: 'Your session expired, please login again.', | ||
53 | }, | ||
54 | serverLogout: { | ||
55 | id: 'login.serverLogout', | ||
56 | defaultMessage: 'Your session expired, please login again.', | ||
57 | }, | ||
58 | signupLink: { | ||
59 | id: 'login.link.signup', | ||
60 | defaultMessage: 'Create a free account', | ||
61 | }, | ||
62 | passwordLink: { | ||
63 | id: 'login.link.password', | ||
64 | defaultMessage: 'Reset password', | ||
65 | }, | ||
66 | }); | ||
67 | |||
68 | interface IProps extends Partial<StoresProps>, WrappedComponentProps { | ||
69 | onSubmit: (...args: any[]) => void; | ||
70 | isSubmitting: boolean; | ||
71 | isTokenExpired: boolean; | ||
72 | isServerLogout: boolean; | ||
73 | signupRoute: string; | ||
74 | // eslint-disable-next-line react/no-unused-prop-types | ||
75 | passwordRoute: string; // TODO: Uncomment this line after fixing password recovery in-app | ||
76 | error: GlobalError; | ||
77 | } | ||
78 | |||
79 | @inject('actions') | ||
80 | @observer | ||
81 | class Login extends Component<IProps> { | ||
82 | form: Form; | ||
83 | |||
84 | constructor(props: IProps) { | ||
85 | super(props); | ||
86 | |||
87 | this.form = new Form({ | ||
88 | fields: { | ||
89 | email: { | ||
90 | label: this.props.intl.formatMessage(messages.emailLabel), | ||
91 | value: '', | ||
92 | validators: [required, email], | ||
93 | }, | ||
94 | password: { | ||
95 | label: this.props.intl.formatMessage(messages.passwordLabel), | ||
96 | value: '', | ||
97 | validators: [required], | ||
98 | type: 'password', | ||
99 | }, | ||
100 | }, | ||
101 | }); | ||
102 | } | ||
103 | |||
104 | submit(e: FormEvent<HTMLFormElement>): void { | ||
105 | e.preventDefault(); | ||
106 | this.form.submit({ | ||
107 | onSuccess: form => { | ||
108 | this.props.onSubmit(form.values()); | ||
109 | }, | ||
110 | onError: () => {}, | ||
111 | }); | ||
112 | } | ||
113 | |||
114 | render(): ReactElement { | ||
115 | const { form } = this; | ||
116 | const { | ||
117 | isSubmitting, | ||
118 | isTokenExpired, | ||
119 | isServerLogout, | ||
120 | signupRoute, | ||
121 | error, | ||
122 | intl, | ||
123 | // passwordRoute, // TODO: Uncomment this line after fixing password recovery in-app | ||
124 | } = this.props; | ||
125 | |||
126 | return ( | ||
127 | <div className="auth__container"> | ||
128 | <form className="franz-form auth__form" onSubmit={e => this.submit(e)}> | ||
129 | <Link to="/auth/welcome"> | ||
130 | <img src="./assets/images/logo.svg" className="auth__logo" alt="" /> | ||
131 | </Link> | ||
132 | <H1>{intl.formatMessage(messages.headline)}</H1> | ||
133 | {isTokenExpired && ( | ||
134 | <p className="error-message center"> | ||
135 | {intl.formatMessage(messages.tokenExpired)} | ||
136 | </p> | ||
137 | )} | ||
138 | {isServerLogout && ( | ||
139 | <p className="error-message center"> | ||
140 | {intl.formatMessage(messages.serverLogout)} | ||
141 | </p> | ||
142 | )} | ||
143 | <Input field={form.$('email')} focus /> | ||
144 | <Input field={form.$('password')} showPasswordToggle /> | ||
145 | {error.code === 'invalid-credentials' && ( | ||
146 | <> | ||
147 | <p className="error-message center"> | ||
148 | {intl.formatMessage(messages.invalidCredentials)} | ||
149 | </p> | ||
150 | {window['ferdium'].stores.settings.all.app.server !== | ||
151 | LIVE_FRANZ_API && ( | ||
152 | <p className="error-message center"> | ||
153 | {intl.formatMessage(messages.customServerQuestion)}{' '} | ||
154 | <Link | ||
155 | to={`${window[ | ||
156 | 'ferdium' | ||
157 | ].stores.settings.all.app.server.replace( | ||
158 | API_VERSION, | ||
159 | '', | ||
160 | )}/import`} | ||
161 | target="_blank" | ||
162 | style={{ cursor: 'pointer', textDecoration: 'underline' }} | ||
163 | > | ||
164 | {intl.formatMessage(messages.customServerSuggestion)} | ||
165 | </Link> | ||
166 | </p> | ||
167 | )} | ||
168 | </> | ||
169 | )} | ||
170 | {isSubmitting ? ( | ||
171 | <Button | ||
172 | className="auth__button is-loading" | ||
173 | buttonType="secondary" | ||
174 | label={`${intl.formatMessage(messages.submitButtonLabel)} ...`} | ||
175 | loaded={false} | ||
176 | disabled | ||
177 | onClick={noop} | ||
178 | /> | ||
179 | ) : ( | ||
180 | <Button | ||
181 | type="submit" | ||
182 | className="auth__button" | ||
183 | label={intl.formatMessage(messages.submitButtonLabel)} | ||
184 | onClick={noop} | ||
185 | /> | ||
186 | )} | ||
187 | </form> | ||
188 | <div className="auth__links"> | ||
189 | <Link to={signupRoute}> | ||
190 | {intl.formatMessage(messages.signupLink)} | ||
191 | </Link> | ||
192 | <Link | ||
193 | // to={passwordRoute} // TODO: Uncomment this line after fixing password recovery in-app | ||
194 | to={`${serverBase()}/user/forgot`} // TODO: Remove this line after fixing password recovery in-app | ||
195 | target="_blank" // TODO: Remove this line after fixing password recovery in-app | ||
196 | > | ||
197 | {intl.formatMessage(messages.passwordLink)} | ||
198 | </Link> | ||
199 | </div> | ||
200 | <div className="auth__help"> | ||
201 | <Link to="/auth/welcome"> | ||
202 | <Icon icon={mdiArrowLeftCircle} size={1.5} /> | ||
203 | </Link> | ||
204 | </div> | ||
205 | </div> | ||
206 | ); | ||
207 | } | ||
208 | } | ||
209 | |||
210 | export default injectIntl(Login); | ||