From 65be5c7833baa6ff22653e20d19200bfe20924d5 Mon Sep 17 00:00:00 2001 From: Dessalines Date: Sun, 19 Sep 2021 16:31:17 -0400 Subject: [PATCH] Splitting login and signup pages. Fixes #386 (#431) --- src/shared/components/app/navbar.tsx | 24 +- src/shared/components/home/login.tsx | 351 +------------------- src/shared/components/home/signup.tsx | 444 ++++++++++++++++++++++++++ src/shared/routes.ts | 5 + 4 files changed, 470 insertions(+), 354 deletions(-) create mode 100644 src/shared/components/home/signup.tsx diff --git a/src/shared/components/app/navbar.tsx b/src/shared/components/app/navbar.tsx index ceb497ff..50bf5fa1 100644 --- a/src/shared/components/app/navbar.tsx +++ b/src/shared/components/app/navbar.tsx @@ -264,7 +264,7 @@ export class Navbar extends Component { /^\/search/ ) && (
{ ) : ( @@ -482,6 +491,11 @@ export class Navbar extends Component { i.context.router.history.push(`/login`); } + handleGotoSignup(i: Navbar) { + i.setState({ showDropdown: false, expanded: false }); + i.context.router.history.push(`/signup`); + } + handleShowDropdown(i: Navbar) { i.state.showDropdown = !i.state.showDropdown; i.setState(i.state); diff --git a/src/shared/components/home/login.tsx b/src/shared/components/home/login.tsx index c6338af4..73d7dbeb 100644 --- a/src/shared/components/home/login.tsx +++ b/src/shared/components/home/login.tsx @@ -1,14 +1,9 @@ -import { Options, passwordStrength } from "check-password-strength"; -import { I18nKeys } from "i18next"; import { Component, linkEvent } from "inferno"; -import { T } from "inferno-i18next-dess"; import { - GetCaptchaResponse, GetSiteResponse, Login as LoginForm, LoginResponse, PasswordReset, - Register, SiteView, UserOperation, } from "lemmy-js-client"; @@ -18,7 +13,6 @@ import { UserService, WebSocketService } from "../../services"; import { authField, isBrowser, - joinLemmyUrl, setIsoData, toast, validEmail, @@ -28,67 +22,24 @@ import { wsUserOp, } from "../../utils"; import { HtmlTags } from "../common/html-tags"; -import { Icon, Spinner } from "../common/icon"; - -const passwordStrengthOptions: Options = [ - { - id: 0, - value: "too_weak", - minDiversity: 0, - minLength: 0, - }, - { - id: 1, - value: "weak", - minDiversity: 2, - minLength: 10, - }, - { - id: 2, - value: "medium", - minDiversity: 3, - minLength: 12, - }, - { - id: 3, - value: "strong", - minDiversity: 4, - minLength: 14, - }, -]; +import { Spinner } from "../common/icon"; interface State { loginForm: LoginForm; - registerForm: Register; loginLoading: boolean; - registerLoading: boolean; - captcha: GetCaptchaResponse; - captchaPlaying: boolean; site_view: SiteView; } export class Login extends Component { private isoData = setIsoData(this.context); private subscription: Subscription; - private audio: HTMLAudioElement; emptyState: State = { loginForm: { username_or_email: undefined, password: undefined, }, - registerForm: { - username: undefined, - password: undefined, - password_verify: undefined, - show_nsfw: false, - captcha_uuid: undefined, - captcha_answer: undefined, - }, loginLoading: false, - registerLoading: false, - captcha: undefined, - captchaPlaying: false, site_view: this.isoData.site_res.site_view, }; @@ -134,8 +85,7 @@ export class Login extends Component { path={this.context.router.route.match.url} />
-
{this.loginForm()}
-
{this.registerForm()}
+
{this.loginForm()}
); @@ -204,213 +154,6 @@ export class Login extends Component { ); } - registerForm() { - return ( - -
{i18n.t("sign_up")}
- -
- - -
- -
-
- -
- -
- - {!validEmail(this.state.registerForm.email) && ( - - )} -
-
- -
- -
- - {this.state.registerForm.password && ( -
- {i18n.t(this.passwordStrength as I18nKeys)} -
- )} -
-
- -
- -
- -
-
- - {this.state.captcha && ( -
- - {this.showCaptcha()} -
- -
-
- )} - {this.state.site_view.site.enable_nsfw && ( -
-
-
- - -
-
-
- )} - {this.isLemmyMl && ( - - )} -
-
- -
-
- - ); - } - - showCaptcha() { - return ( -
- {this.state.captcha.ok && ( - <> - {i18n.t("captcha")} - {this.state.captcha.ok.wav && ( - - )} - - )} -
- ); - } - - get passwordStrength() { - return passwordStrength( - this.state.registerForm.password, - passwordStrengthOptions - ).value; - } - - get passwordColorClass(): string { - let strength = this.passwordStrength; - - if (["weak", "medium"].includes(strength)) { - return "text-warning"; - } else if (strength == "strong") { - return "text-success"; - } else { - return "text-danger"; - } - } - handleLoginSubmit(i: Login, event: any) { event.preventDefault(); i.state.loginLoading = true; @@ -428,53 +171,6 @@ export class Login extends Component { i.setState(i.state); } - handleRegisterSubmit(i: Login, event: any) { - event.preventDefault(); - i.state.registerLoading = true; - i.setState(i.state); - WebSocketService.Instance.send(wsClient.register(i.state.registerForm)); - } - - handleRegisterUsernameChange(i: Login, event: any) { - i.state.registerForm.username = event.target.value; - i.setState(i.state); - } - - handleRegisterEmailChange(i: Login, event: any) { - i.state.registerForm.email = event.target.value; - if (i.state.registerForm.email == "") { - i.state.registerForm.email = undefined; - } - i.setState(i.state); - } - - handleRegisterPasswordChange(i: Login, event: any) { - i.state.registerForm.password = event.target.value; - i.setState(i.state); - } - - handleRegisterPasswordVerifyChange(i: Login, event: any) { - i.state.registerForm.password_verify = event.target.value; - i.setState(i.state); - } - - handleRegisterShowNsfwChange(i: Login, event: any) { - i.state.registerForm.show_nsfw = event.target.checked; - i.setState(i.state); - } - - handleRegisterCaptchaAnswerChange(i: Login, event: any) { - i.state.registerForm.captcha_answer = event.target.value; - i.setState(i.state); - } - - handleRegenCaptcha(i: Login) { - i.audio = null; - i.state.captchaPlaying = false; - i.setState(i.state); - WebSocketService.Instance.send(wsClient.getCaptcha()); - } - handlePasswordReset(i: Login, event: any) { event.preventDefault(); let resetForm: PasswordReset = { @@ -483,37 +179,12 @@ export class Login extends Component { WebSocketService.Instance.send(wsClient.passwordReset(resetForm)); } - handleCaptchaPlay(i: Login) { - // This was a bad bug, it should only build the new audio on a new file. - // Replays would stop prematurely if this was rebuilt every time. - if (i.audio == null) { - let base64 = `data:audio/wav;base64,${i.state.captcha.ok.wav}`; - i.audio = new Audio(base64); - } - - i.audio.play(); - - i.state.captchaPlaying = true; - i.setState(i.state); - - i.audio.addEventListener("ended", () => { - i.audio.currentTime = 0; - i.state.captchaPlaying = false; - i.setState(i.state); - }); - } - - captchaPngSrc() { - return `data:image/png;base64,${this.state.captcha.ok.png}`; - } - parseMessage(msg: any) { let op = wsUserOp(msg); console.log(msg); if (msg.error) { toast(i18n.t(msg.error), "danger"); this.state = this.emptyState; - this.state.registerForm.captcha_answer = undefined; // Refetch another captcha WebSocketService.Instance.send(wsClient.getCaptcha()); this.setState(this.state); @@ -531,24 +202,6 @@ export class Login extends Component { ); toast(i18n.t("logged_in")); this.props.history.push("/"); - } else if (op == UserOperation.Register) { - let data = wsJsonToRes(msg).data; - this.state = this.emptyState; - this.setState(this.state); - UserService.Instance.login(data); - WebSocketService.Instance.send( - wsClient.userJoin({ - auth: authField(), - }) - ); - this.props.history.push("/communities"); - } else if (op == UserOperation.GetCaptcha) { - let data = wsJsonToRes(msg).data; - if (data.ok) { - this.state.captcha = data; - this.state.registerForm.captcha_uuid = data.ok.uuid; - this.setState(this.state); - } } else if (op == UserOperation.PasswordReset) { toast(i18n.t("reset_password_mail_sent")); } else if (op == UserOperation.GetSite) { diff --git a/src/shared/components/home/signup.tsx b/src/shared/components/home/signup.tsx new file mode 100644 index 00000000..722f644f --- /dev/null +++ b/src/shared/components/home/signup.tsx @@ -0,0 +1,444 @@ +import { Component, linkEvent } from "inferno"; +import { T } from "inferno-i18next-dess"; +import { + GetCaptchaResponse, + GetSiteResponse, + LoginResponse, + Register, + SiteView, + UserOperation, +} from "lemmy-js-client"; +import { Subscription } from "rxjs"; +import { i18n } from "../../i18next"; +import { Options, passwordStrength } from "check-password-strength"; +import { UserService, WebSocketService } from "../../services"; +import { + authField, + isBrowser, + joinLemmyUrl, + setIsoData, + toast, + validEmail, + wsClient, + wsJsonToRes, + wsSubscribe, + wsUserOp, +} from "../../utils"; +import { HtmlTags } from "../common/html-tags"; +import { Icon, Spinner } from "../common/icon"; +import {I18nKeys} from "i18next"; + +const passwordStrengthOptions: Options = [ + { + id: 0, + value: "too_weak", + minDiversity: 0, + minLength: 0, + }, + { + id: 1, + value: "weak", + minDiversity: 2, + minLength: 10, + }, + { + id: 2, + value: "medium", + minDiversity: 3, + minLength: 12, + }, + { + id: 3, + value: "strong", + minDiversity: 4, + minLength: 14, + }, +]; + +interface State { + registerForm: Register; + registerLoading: boolean; + captcha: GetCaptchaResponse; + captchaPlaying: boolean; + site_view: SiteView; +} + +export class Signup extends Component { + private isoData = setIsoData(this.context); + private subscription: Subscription; + private audio: HTMLAudioElement; + + emptyState: State = { + registerForm: { + username: undefined, + password: undefined, + password_verify: undefined, + show_nsfw: false, + captcha_uuid: undefined, + captcha_answer: undefined, + }, + registerLoading: false, + captcha: undefined, + captchaPlaying: false, + site_view: this.isoData.site_res.site_view, + }; + + constructor(props: any, context: any) { + super(props, context); + + this.state = this.emptyState; + + this.parseMessage = this.parseMessage.bind(this); + this.subscription = wsSubscribe(this.parseMessage); + + if (isBrowser()) { + WebSocketService.Instance.send(wsClient.getCaptcha()); + } + } + + componentWillUnmount() { + if (isBrowser()) { + this.subscription.unsubscribe(); + } + } + + get documentTitle(): string { + return `${i18n.t("login")} - ${this.state.site_view.site.name}`; + } + + get isLemmyMl(): boolean { + return isBrowser() && window.location.hostname == "lemmy.ml"; + } + + render() { + return ( +
+ +
+
{this.registerForm()}
+
+
+ ); + } + + registerForm() { + return ( +
+
{i18n.t("sign_up")}
+ +
+ + +
+ +
+
+ +
+ +
+ + {!validEmail(this.state.registerForm.email) && ( + + )} +
+
+ +
+ +
+ + {this.state.registerForm.password && ( +
+ {i18n.t(this.passwordStrength as I18nKeys)} +
+ )} +
+
+ +
+ +
+ +
+
+ + {this.state.captcha && ( +
+ + {this.showCaptcha()} +
+ +
+
+ )} + {this.state.site_view.site.enable_nsfw && ( +
+
+
+ + +
+
+
+ )} + {this.isLemmyMl && ( + + )} +
+
+ +
+
+
+ ); + } + + showCaptcha() { + return ( +
+ {this.state.captcha.ok && ( + <> + {i18n.t("captcha")} + {this.state.captcha.ok.wav && ( + + )} + + )} +
+ ); + } + + get passwordStrength() { + return passwordStrength( + this.state.registerForm.password, + passwordStrengthOptions + ).value; + } + + get passwordColorClass(): string { + let strength = this.passwordStrength; + + if (["weak", "medium"].includes(strength)) { + return "text-warning"; + } else if (strength == "strong") { + return "text-success"; + } else { + return "text-danger"; + } + } + + handleRegisterSubmit(i: Signup, event: any) { + event.preventDefault(); + i.state.registerLoading = true; + i.setState(i.state); + WebSocketService.Instance.send(wsClient.register(i.state.registerForm)); + } + + handleRegisterUsernameChange(i: Signup, event: any) { + i.state.registerForm.username = event.target.value; + i.setState(i.state); + } + + handleRegisterEmailChange(i: Signup, event: any) { + i.state.registerForm.email = event.target.value; + if (i.state.registerForm.email == "") { + i.state.registerForm.email = undefined; + } + i.setState(i.state); + } + + handleRegisterPasswordChange(i: Signup, event: any) { + i.state.registerForm.password = event.target.value; + i.setState(i.state); + } + + handleRegisterPasswordVerifyChange(i: Signup, event: any) { + i.state.registerForm.password_verify = event.target.value; + i.setState(i.state); + } + + handleRegisterShowNsfwChange(i: Signup, event: any) { + i.state.registerForm.show_nsfw = event.target.checked; + i.setState(i.state); + } + + handleRegisterCaptchaAnswerChange(i: Signup, event: any) { + i.state.registerForm.captcha_answer = event.target.value; + i.setState(i.state); + } + + handleRegenCaptcha(i: Signup) { + i.audio = null; + i.state.captchaPlaying = false; + i.setState(i.state); + WebSocketService.Instance.send(wsClient.getCaptcha()); + } + + handleCaptchaPlay(i: Signup) { + // This was a bad bug, it should only build the new audio on a new file. + // Replays would stop prematurely if this was rebuilt every time. + if (i.audio == null) { + let base64 = `data:audio/wav;base64,${i.state.captcha.ok.wav}`; + i.audio = new Audio(base64); + } + + i.audio.play(); + + i.state.captchaPlaying = true; + i.setState(i.state); + + i.audio.addEventListener("ended", () => { + i.audio.currentTime = 0; + i.state.captchaPlaying = false; + i.setState(i.state); + }); + } + + captchaPngSrc() { + return `data:image/png;base64,${this.state.captcha.ok.png}`; + } + + parseMessage(msg: any) { + let op = wsUserOp(msg); + console.log(msg); + if (msg.error) { + toast(i18n.t(msg.error), "danger"); + this.state = this.emptyState; + this.state.registerForm.captcha_answer = undefined; + // Refetch another captcha + WebSocketService.Instance.send(wsClient.getCaptcha()); + this.setState(this.state); + return; + } else { + if (op == UserOperation.Register) { + let data = wsJsonToRes(msg).data; + this.state = this.emptyState; + this.setState(this.state); + UserService.Instance.login(data); + WebSocketService.Instance.send( + wsClient.userJoin({ + auth: authField(), + }) + ); + this.props.history.push("/communities"); + } else if (op == UserOperation.GetCaptcha) { + let data = wsJsonToRes(msg).data; + if (data.ok) { + this.state.captcha = data; + this.state.registerForm.captcha_uuid = data.ok.uuid; + this.setState(this.state); + } + } else if (op == UserOperation.PasswordReset) { + toast(i18n.t("reset_password_mail_sent")); + } else if (op == UserOperation.GetSite) { + let data = wsJsonToRes(msg).data; + this.state.site_view = data.site_view; + this.setState(this.state); + } + } + } +} diff --git a/src/shared/routes.ts b/src/shared/routes.ts index 92c16960..2681b234 100644 --- a/src/shared/routes.ts +++ b/src/shared/routes.ts @@ -8,6 +8,7 @@ import { Instances } from "./components/home/instances"; import { Login } from "./components/home/login"; import { PasswordChange } from "./components/home/password_change"; import { Setup } from "./components/home/setup"; +import { Signup } from "./components/home/signup"; import { Modlog } from "./components/modlog"; import { Inbox } from "./components/person/inbox"; import { Profile } from "./components/person/profile"; @@ -38,6 +39,10 @@ export const routes: IRoutePropsWithFetch[] = [ path: `/login`, component: Login, }, + { + path: `/signup`, + component: Signup, + }, { path: `/create_post`, component: CreatePost,