1 import { Component, linkEvent } from 'inferno';
2 import { Helmet } from 'inferno-helmet';
3 import { Subscription } from 'rxjs';
4 import { retryWhen, delay, take } from 'rxjs/operators';
12 WebSocketJsonResponse,
14 } from '../interfaces';
15 import { WebSocketService, UserService } from '../services';
16 import { wsJsonToRes, validEmail, toast } from '../utils';
17 import { i18n } from '../i18next';
21 registerForm: RegisterForm;
22 loginLoading: boolean;
23 registerLoading: boolean;
32 export class Login extends Component<any, State> {
33 private subscription: Subscription;
37 username_or_email: undefined,
43 password_verify: undefined,
48 registerLoading: false,
50 a: Math.floor(Math.random() * 10) + 1,
51 b: Math.floor(Math.random() * 10) + 1,
57 creator_id: undefined,
59 creator_name: undefined,
60 number_of_users: undefined,
61 number_of_posts: undefined,
62 number_of_comments: undefined,
63 number_of_communities: undefined,
64 enable_downvotes: undefined,
65 open_registration: undefined,
66 enable_nsfw: undefined,
70 constructor(props: any, context: any) {
71 super(props, context);
73 this.state = this.emptyState;
75 this.subscription = WebSocketService.Instance.subject
76 .pipe(retryWhen(errors => errors.pipe(delay(3000), take(10))))
78 msg => this.parseMessage(msg),
79 err => console.error(err),
80 () => console.log('complete')
83 WebSocketService.Instance.getSite();
86 componentWillUnmount() {
87 this.subscription.unsubscribe();
90 get documentTitle(): string {
91 if (this.state.site.name) {
92 return `${i18n.t('login')} - ${this.state.site.name}`;
100 <div class="container">
101 <Helmet title={this.documentTitle} />
103 <div class="col-12 col-lg-6 mb-4">{this.loginForm()}</div>
104 <div class="col-12 col-lg-6">{this.registerForm()}</div>
113 <form onSubmit={linkEvent(this, this.handleLoginSubmit)}>
114 <h5>{i18n.t('login')}</h5>
115 <div class="form-group row">
117 class="col-sm-2 col-form-label"
118 htmlFor="login-email-or-username"
120 {i18n.t('email_or_username')}
122 <div class="col-sm-10">
126 id="login-email-or-username"
127 value={this.state.loginForm.username_or_email}
128 onInput={linkEvent(this, this.handleLoginUsernameChange)}
134 <div class="form-group row">
135 <label class="col-sm-2 col-form-label" htmlFor="login-password">
138 <div class="col-sm-10">
142 value={this.state.loginForm.password}
143 onInput={linkEvent(this, this.handleLoginPasswordChange)}
147 {validEmail(this.state.loginForm.username_or_email) && (
150 onClick={linkEvent(this, this.handlePasswordReset)}
151 className="btn p-0 btn-link d-inline-block float-right text-muted small font-weight-bold"
153 {i18n.t('forgot_password')}
158 <div class="form-group row">
159 <div class="col-sm-10">
160 <button type="submit" class="btn btn-secondary">
161 {this.state.loginLoading ? (
162 <svg class="icon icon-spinner spin">
163 <use xlinkHref="#icon-spinner"></use>
177 <form onSubmit={linkEvent(this, this.handleRegisterSubmit)}>
178 <h5>{i18n.t('sign_up')}</h5>
180 <div class="form-group row">
181 <label class="col-sm-2 col-form-label" htmlFor="register-username">
185 <div class="col-sm-10">
188 id="register-username"
190 value={this.state.registerForm.username}
191 onInput={linkEvent(this, this.handleRegisterUsernameChange)}
195 pattern="[a-zA-Z0-9_]+"
200 <div class="form-group row">
201 <label class="col-sm-2 col-form-label" htmlFor="register-email">
204 <div class="col-sm-10">
209 placeholder={i18n.t('optional')}
210 value={this.state.registerForm.email}
211 onInput={linkEvent(this, this.handleRegisterEmailChange)}
214 {!validEmail(this.state.registerForm.email) && (
215 <div class="mt-2 mb-0 alert alert-light" role="alert">
216 <svg class="icon icon-inline mr-2">
217 <use xlinkHref="#icon-alert-triangle"></use>
219 {i18n.t('no_password_reset')}
225 <div class="form-group row">
226 <label class="col-sm-2 col-form-label" htmlFor="register-password">
229 <div class="col-sm-10">
232 id="register-password"
233 value={this.state.registerForm.password}
234 autoComplete="new-password"
235 onInput={linkEvent(this, this.handleRegisterPasswordChange)}
242 <div class="form-group row">
244 class="col-sm-2 col-form-label"
245 htmlFor="register-verify-password"
247 {i18n.t('verify_password')}
249 <div class="col-sm-10">
252 id="register-verify-password"
253 value={this.state.registerForm.password_verify}
254 autoComplete="new-password"
255 onInput={linkEvent(this, this.handleRegisterPasswordVerifyChange)}
261 <div class="form-group row">
262 <label class="col-sm-10 col-form-label" htmlFor="register-math">
263 {i18n.t('what_is')}{' '}
264 {`${this.state.mathQuestion.a} + ${this.state.mathQuestion.b}?`}
267 <div class="col-sm-2">
272 value={this.state.mathQuestion.answer}
273 onInput={linkEvent(this, this.handleMathAnswerChange)}
278 {this.state.site.enable_nsfw && (
279 <div class="form-group row">
280 <div class="col-sm-10">
281 <div class="form-check">
283 class="form-check-input"
284 id="register-show-nsfw"
286 checked={this.state.registerForm.show_nsfw}
287 onChange={linkEvent(this, this.handleRegisterShowNsfwChange)}
289 <label class="form-check-label" htmlFor="register-show-nsfw">
290 {i18n.t('show_nsfw')}
296 <div class="form-group row">
297 <div class="col-sm-10">
300 class="btn btn-secondary"
301 disabled={this.mathCheck}
303 {this.state.registerLoading ? (
304 <svg class="icon icon-spinner spin">
305 <use xlinkHref="#icon-spinner"></use>
317 handleLoginSubmit(i: Login, event: any) {
318 event.preventDefault();
319 i.state.loginLoading = true;
321 WebSocketService.Instance.login(i.state.loginForm);
324 handleLoginUsernameChange(i: Login, event: any) {
325 i.state.loginForm.username_or_email = event.target.value;
329 handleLoginPasswordChange(i: Login, event: any) {
330 i.state.loginForm.password = event.target.value;
334 handleRegisterSubmit(i: Login, event: any) {
335 event.preventDefault();
336 i.state.registerLoading = true;
340 WebSocketService.Instance.register(i.state.registerForm);
344 handleRegisterUsernameChange(i: Login, event: any) {
345 i.state.registerForm.username = event.target.value;
349 handleRegisterEmailChange(i: Login, event: any) {
350 i.state.registerForm.email = event.target.value;
351 if (i.state.registerForm.email == '') {
352 i.state.registerForm.email = undefined;
357 handleRegisterPasswordChange(i: Login, event: any) {
358 i.state.registerForm.password = event.target.value;
362 handleRegisterPasswordVerifyChange(i: Login, event: any) {
363 i.state.registerForm.password_verify = event.target.value;
367 handleRegisterShowNsfwChange(i: Login, event: any) {
368 i.state.registerForm.show_nsfw = event.target.checked;
372 handleMathAnswerChange(i: Login, event: any) {
373 i.state.mathQuestion.answer = event.target.value;
377 handlePasswordReset(i: Login) {
378 event.preventDefault();
379 let resetForm: PasswordResetForm = {
380 email: i.state.loginForm.username_or_email,
382 WebSocketService.Instance.passwordReset(resetForm);
385 get mathCheck(): boolean {
387 this.state.mathQuestion.answer !=
388 this.state.mathQuestion.a + this.state.mathQuestion.b
392 parseMessage(msg: WebSocketJsonResponse) {
393 let res = wsJsonToRes(msg);
395 toast(i18n.t(msg.error), 'danger');
396 this.state = this.emptyState;
397 this.setState(this.state);
400 if (res.op == UserOperation.Login) {
401 let data = res.data as LoginResponse;
402 this.state = this.emptyState;
403 this.setState(this.state);
404 UserService.Instance.login(data);
405 WebSocketService.Instance.userJoin();
406 toast(i18n.t('logged_in'));
407 this.props.history.push('/');
408 } else if (res.op == UserOperation.Register) {
409 let data = res.data as LoginResponse;
410 this.state = this.emptyState;
411 this.setState(this.state);
412 UserService.Instance.login(data);
413 WebSocketService.Instance.userJoin();
414 this.props.history.push('/communities');
415 } else if (res.op == UserOperation.PasswordReset) {
416 toast(i18n.t('reset_password_mail_sent'));
417 } else if (res.op == UserOperation.GetSite) {
418 let data = res.data as GetSiteResponse;
419 this.state.site = data.site;
420 this.setState(this.state);