login.tsx 4.8 KB

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