]> Untitled Git - lemmy.git/blob - ui/src/components/login.tsx
Strongly typing WebsocketJsonResponse. Forgot comment-form.tsx
[lemmy.git] / ui / src / components / login.tsx
1 import { Component, linkEvent } from 'inferno';
2 import { Subscription } from 'rxjs';
3 import { retryWhen, delay, take } from 'rxjs/operators';
4 import {
5   LoginForm,
6   RegisterForm,
7   LoginResponse,
8   UserOperation,
9   PasswordResetForm,
10   GetSiteResponse,
11   WebSocketJsonResponse,
12 } from '../interfaces';
13 import { WebSocketService, UserService } from '../services';
14 import { wsJsonToRes, validEmail } from '../utils';
15 import { i18n } from '../i18next';
16 import { T } from 'inferno-i18next';
17
18 interface State {
19   loginForm: LoginForm;
20   registerForm: RegisterForm;
21   loginLoading: boolean;
22   registerLoading: boolean;
23   enable_nsfw: boolean;
24 }
25
26 export class Login extends Component<any, State> {
27   private subscription: Subscription;
28
29   emptyState: State = {
30     loginForm: {
31       username_or_email: undefined,
32       password: undefined,
33     },
34     registerForm: {
35       username: undefined,
36       password: undefined,
37       password_verify: undefined,
38       admin: false,
39       show_nsfw: false,
40     },
41     loginLoading: false,
42     registerLoading: false,
43     enable_nsfw: undefined,
44   };
45
46   constructor(props: any, context: any) {
47     super(props, context);
48
49     this.state = this.emptyState;
50
51     this.subscription = WebSocketService.Instance.subject
52       .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
53       .subscribe(
54         msg => this.parseMessage(msg),
55         err => console.error(err),
56         () => console.log('complete')
57       );
58
59     WebSocketService.Instance.getSite();
60   }
61
62   componentWillUnmount() {
63     this.subscription.unsubscribe();
64   }
65
66   render() {
67     return (
68       <div class="container">
69         <div class="row">
70           <div class="col-12 col-lg-6 mb-4">{this.loginForm()}</div>
71           <div class="col-12 col-lg-6">{this.registerForm()}</div>
72         </div>
73       </div>
74     );
75   }
76
77   loginForm() {
78     return (
79       <div>
80         <form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
81           <h5>Login</h5>
82           <div class="form-group row">
83             <label class="col-sm-2 col-form-label">
84               <T i18nKey="email_or_username">#</T>
85             </label>
86             <div class="col-sm-10">
87               <input
88                 type="text"
89                 class="form-control"
90                 value={this.state.loginForm.username_or_email}
91                 onInput={linkEvent(this, this.handleLoginUsernameChange)}
92                 required
93                 minLength={3}
94               />
95             </div>
96           </div>
97           <div class="form-group row">
98             <label class="col-sm-2 col-form-label">
99               <T i18nKey="password">#</T>
100             </label>
101             <div class="col-sm-10">
102               <input
103                 type="password"
104                 value={this.state.loginForm.password}
105                 onInput={linkEvent(this, this.handleLoginPasswordChange)}
106                 class="form-control"
107                 required
108               />
109               <button
110                 disabled={!validEmail(this.state.loginForm.username_or_email)}
111                 onClick={linkEvent(this, this.handlePasswordReset)}
112                 className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
113               >
114                 <T i18nKey="forgot_password">#</T>
115               </button>
116             </div>
117           </div>
118           <div class="form-group row">
119             <div class="col-sm-10">
120               <button type="submit" class="btn btn-secondary">
121                 {this.state.loginLoading ? (
122                   <svg class="icon icon-spinner spin">
123                     <use xlinkHref="#icon-spinner"></use>
124                   </svg>
125                 ) : (
126                   i18n.t('login')
127                 )}
128               </button>
129             </div>
130           </div>
131         </form>
132       </div>
133     );
134   }
135   registerForm() {
136     return (
137       <form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
138         <h5>
139           <T i18nKey="sign_up">#</T>
140         </h5>
141         <div class="form-group row">
142           <label class="col-sm-2 col-form-label">
143             <T i18nKey="username">#</T>
144           </label>
145           <div class="col-sm-10">
146             <input
147               type="text"
148               class="form-control"
149               value={this.state.registerForm.username}
150               onInput={linkEvent(this, this.handleRegisterUsernameChange)}
151               required
152               minLength={3}
153               maxLength={20}
154               pattern="[a-zA-Z0-9_]+"
155             />
156           </div>
157         </div>
158         <div class="form-group row">
159           <label class="col-sm-2 col-form-label">
160             <T i18nKey="email">#</T>
161           </label>
162           <div class="col-sm-10">
163             <input
164               type="email"
165               class="form-control"
166               placeholder={i18n.t('optional')}
167               value={this.state.registerForm.email}
168               onInput={linkEvent(this, this.handleRegisterEmailChange)}
169               minLength={3}
170             />
171           </div>
172         </div>
173         <div class="form-group row">
174           <label class="col-sm-2 col-form-label">
175             <T i18nKey="password">#</T>
176           </label>
177           <div class="col-sm-10">
178             <input
179               type="password"
180               value={this.state.registerForm.password}
181               onInput={linkEvent(this, this.handleRegisterPasswordChange)}
182               class="form-control"
183               required
184             />
185           </div>
186         </div>
187         <div class="form-group row">
188           <label class="col-sm-2 col-form-label">
189             <T i18nKey="verify_password">#</T>
190           </label>
191           <div class="col-sm-10">
192             <input
193               type="password"
194               value={this.state.registerForm.password_verify}
195               onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
196               class="form-control"
197               required
198             />
199           </div>
200         </div>
201         {this.state.enable_nsfw && (
202           <div class="form-group row">
203             <div class="col-sm-10">
204               <div class="form-check">
205                 <input
206                   class="form-check-input"
207                   type="checkbox"
208                   checked={this.state.registerForm.show_nsfw}
209                   onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
210                 />
211                 <label class="form-check-label">
212                   <T i18nKey="show_nsfw">#</T>
213                 </label>
214               </div>
215             </div>
216           </div>
217         )}
218         <div class="form-group row">
219           <div class="col-sm-10">
220             <button type="submit" class="btn btn-secondary">
221               {this.state.registerLoading ? (
222                 <svg class="icon icon-spinner spin">
223                   <use xlinkHref="#icon-spinner"></use>
224                 </svg>
225               ) : (
226                 i18n.t('sign_up')
227               )}
228             </button>
229           </div>
230         </div>
231       </form>
232     );
233   }
234
235   handleLoginSubmit(i: Login, event: any) {
236     event.preventDefault();
237     i.state.loginLoading = true;
238     i.setState(i.state);
239     WebSocketService.Instance.login(i.state.loginForm);
240   }
241
242   handleLoginUsernameChange(i: Login, event: any) {
243     i.state.loginForm.username_or_email = event.target.value;
244     i.setState(i.state);
245   }
246
247   handleLoginPasswordChange(i: Login, event: any) {
248     i.state.loginForm.password = event.target.value;
249     i.setState(i.state);
250   }
251
252   handleRegisterSubmit(i: Login, event: any) {
253     event.preventDefault();
254     i.state.registerLoading = true;
255     i.setState(i.state);
256
257     WebSocketService.Instance.register(i.state.registerForm);
258   }
259
260   handleRegisterUsernameChange(i: Login, event: any) {
261     i.state.registerForm.username = event.target.value;
262     i.setState(i.state);
263   }
264
265   handleRegisterEmailChange(i: Login, event: any) {
266     i.state.registerForm.email = event.target.value;
267     if (i.state.registerForm.email == '') {
268       i.state.registerForm.email = undefined;
269     }
270     i.setState(i.state);
271   }
272
273   handleRegisterPasswordChange(i: Login, event: any) {
274     i.state.registerForm.password = event.target.value;
275     i.setState(i.state);
276   }
277
278   handleRegisterPasswordVerifyChange(i: Login, event: any) {
279     i.state.registerForm.password_verify = event.target.value;
280     i.setState(i.state);
281   }
282
283   handleRegisterShowNsfwChange(i: Login, event: any) {
284     i.state.registerForm.show_nsfw = event.target.checked;
285     i.setState(i.state);
286   }
287
288   handlePasswordReset(i: Login) {
289     event.preventDefault();
290     let resetForm: PasswordResetForm = {
291       email: i.state.loginForm.username_or_email,
292     };
293     WebSocketService.Instance.passwordReset(resetForm);
294   }
295
296   parseMessage(msg: WebSocketJsonResponse) {
297     let res = wsJsonToRes(msg);
298     if (res.error) {
299       alert(i18n.t(res.error));
300       this.state = this.emptyState;
301       this.setState(this.state);
302       return;
303     } else {
304       if (res.op == UserOperation.Login) {
305         let data = res.data as LoginResponse;
306         this.state = this.emptyState;
307         this.setState(this.state);
308         UserService.Instance.login(data);
309         this.props.history.push('/');
310       } else if (res.op == UserOperation.Register) {
311         let data = res.data as LoginResponse;
312         this.state = this.emptyState;
313         this.setState(this.state);
314         UserService.Instance.login(data);
315         this.props.history.push('/communities');
316       } else if (res.op == UserOperation.PasswordReset) {
317         alert(i18n.t('reset_password_mail_sent'));
318       } else if (res.op == UserOperation.GetSite) {
319         let data = res.data as GetSiteResponse;
320         this.state.enable_nsfw = data.site.enable_nsfw;
321         this.setState(this.state);
322         document.title = `${i18n.t('login')} - ${
323           WebSocketService.Instance.site.name
324         }`;
325       }
326     }
327   }
328 }