login.tsx 4.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168
  1. import React, { useEffect, useState } from "react";
  2. import Router from "next/router";
  3. import Link from "next/link";
  4. import axios from "axios";
  5. import styled from "styled-components";
  6. import emailValidator from "email-validator";
  7. import { useFormState } from "react-use-form-state";
  8. import { Flex } from "reflexbox/styled-components";
  9. import { useStoreState, useStoreActions } from "../store";
  10. import { ColCenterV } from "../components/Layout";
  11. import AppWrapper from "../components/AppWrapper";
  12. import { fadeIn } from "../helpers/animations";
  13. import TextInput from "../components/TextInput";
  14. import { Button } from "../components/Button";
  15. import ALink from "../components/ALink";
  16. import Text, { H2 } from "../components/Text";
  17. import { API } from "../consts";
  18. const LoginForm = styled(Flex).attrs({
  19. as: "form",
  20. flexDirection: "column"
  21. })`
  22. animation: ${fadeIn} 0.8s ease-out;
  23. `;
  24. const Email = styled.span`
  25. font-weight: normal;
  26. color: #512da8;
  27. border-bottom: 1px dotted #999;
  28. `;
  29. const LoginPage = () => {
  30. const { isAuthenticated } = useStoreState(s => s.auth);
  31. const login = useStoreActions(s => s.auth.login);
  32. const [error, setError] = useState("");
  33. const [verifying, setVerifying] = useState(false);
  34. const [loading, setLoading] = useState({ login: false, signup: false });
  35. const [formState, { email, password, label }] = useFormState<{
  36. email: string;
  37. password: string;
  38. }>(null, { withIds: true });
  39. useEffect(() => {
  40. if (isAuthenticated) Router.push("/");
  41. }, [isAuthenticated]);
  42. function onSubmit(type: "login" | "signup") {
  43. return async e => {
  44. e.preventDefault();
  45. const { email, password } = formState.values;
  46. if (loading.login || loading.signup) return null;
  47. if (!email) {
  48. return setError("Email address must not be empty.");
  49. }
  50. if (!emailValidator.validate(email)) {
  51. return setError("Email address is not valid.");
  52. }
  53. if (password.trim().length < 8) {
  54. return setError("Password must be at least 8 chars long.");
  55. }
  56. setError("");
  57. if (type === "login") {
  58. setLoading(s => ({ ...s, login: true }));
  59. try {
  60. await login(formState.values);
  61. Router.push("/");
  62. } catch (error) {
  63. setError(error.response.data.error);
  64. }
  65. }
  66. if (type === "signup") {
  67. setLoading(s => ({ ...s, signup: true }));
  68. try {
  69. await axios.post(API.SIGNUP, { email, password });
  70. setVerifying(true);
  71. } catch (error) {
  72. setError(error.response.data.error);
  73. }
  74. }
  75. setLoading({ login: false, signup: false });
  76. };
  77. }
  78. if (isAuthenticated) {
  79. return null;
  80. }
  81. return (
  82. <AppWrapper>
  83. <ColCenterV flex="0 0 auto" mt={24} mb={64}>
  84. {verifying ? (
  85. <H2 textAlign="center" light>
  86. A verification email has been sent to{" "}
  87. <Email>{formState.values.email}</Email>.
  88. </H2>
  89. ) : (
  90. <LoginForm id="login-form" onSubmit={onSubmit("login")}>
  91. <Text {...label("email")} as="label" mb={2} bold>
  92. Email address:
  93. </Text>
  94. <TextInput
  95. {...email("email")}
  96. placeholder="Email address..."
  97. height={[56, 64, 72]}
  98. mb={[24, 32, 36]}
  99. autoFocus
  100. />
  101. <Text {...label("password")} as="label" mb={2} bold>
  102. Password (min chars: 8):
  103. </Text>
  104. <TextInput
  105. {...password("password")}
  106. placeholder="Password..."
  107. height={[56, 64, 72]}
  108. mb={[24, 32, 36]}
  109. />
  110. <Flex justifyContent="center">
  111. <Button
  112. flex="1 1 auto"
  113. mr={["8px", 16]}
  114. height={[44, 56]}
  115. icon={loading.login ? "loader" : "login"}
  116. onClick={onSubmit("login")}
  117. >
  118. Log in
  119. </Button>
  120. <Button
  121. flex="1 1 auto"
  122. ml={["8px", 16]}
  123. height={[44, 56]}
  124. icon={loading.signup ? "loader" : "signup"}
  125. color="purple"
  126. onClick={onSubmit("signup")}
  127. >
  128. Sign up
  129. </Button>
  130. </Flex>
  131. <Link href="/reset-password">
  132. <ALink
  133. href="/reset-password"
  134. title="Forget password"
  135. fontSize={14}
  136. alignSelf="flex-start"
  137. my={16}
  138. >
  139. Forgot your password?
  140. </ALink>
  141. </Link>
  142. <Text color="red" mt={1} normal>
  143. {error}
  144. </Text>
  145. </LoginForm>
  146. )}
  147. </ColCenterV>
  148. </AppWrapper>
  149. );
  150. };
  151. export default LoginPage;