diff --git a/src/components/AuthenticationFlow/EmailLoginForm.tsx b/src/components/AuthenticationFlow/EmailLoginForm.tsx index 99b632368..49b4e46a4 100644 --- a/src/components/AuthenticationFlow/EmailLoginForm.tsx +++ b/src/components/AuthenticationFlow/EmailLoginForm.tsx @@ -2,7 +2,7 @@ import Cookies from 'js-cookie'; import type { FunctionComponent } from 'preact'; import { useState } from 'preact/hooks'; import { httpPost } from '../../lib/http'; -import {TOKEN_COOKIE_NAME} from "../../lib/jwt"; +import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; const EmailLoginForm: FunctionComponent<{}> = () => { const [email, setEmail] = useState(''); @@ -16,44 +16,32 @@ const EmailLoginForm: FunctionComponent<{}> = () => { setIsLoading(true); setError(''); - fetch(`${import.meta.env.PUBLIC_API_URL}/v1-login`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ + const { response, error } = await httpPost<{ token: string }>( + `${import.meta.env.PUBLIC_API_URL}/v1-login`, + { email, password, - }), - }) - .then((res) => res.json()) - .then((data) => { - if (data.type === 'user_not_verified') { - window.location.href = `/verification-pending?email=${encodeURIComponent( - email - )}`; - return; - } + } + ); - if (!data.token) { - setIsLoading(false); - setError( - data.message || 'Something went wrong. Please try again later.' - ); - return; - } + // Log the user in and reload the page + if (response?.token) { + Cookies.set(TOKEN_COOKIE_NAME, response.token); + window.location.reload(); - // If the response is ok, we'll set the token in a cookie - Cookies.set(TOKEN_COOKIE_NAME, data.token); - // Refreshing will automatically redirect to the relevant page - // Doing this to avoid redirecting to home page from any pages - // such as roadmap, best-practice etc - window.location.reload(); - }) - .catch((err) => { - setIsLoading(false); - setError('Something went wrong. Please try again later.'); - }); + return; + } + + // @todo use proper types + if ((error as any).type === 'user_not_verified') { + window.location.href = `/verification-pending?email=${encodeURIComponent( + email + )}`; + return; + } + + setIsLoading(false); + setError(error?.message || 'Something went wrong. Please try again later.'); }; return ( diff --git a/src/components/AuthenticationFlow/GitHubButton.tsx b/src/components/AuthenticationFlow/GitHubButton.tsx index 0f6ada813..79f2c935f 100644 --- a/src/components/AuthenticationFlow/GitHubButton.tsx +++ b/src/components/AuthenticationFlow/GitHubButton.tsx @@ -4,6 +4,7 @@ import GitHubIcon from '../../icons/github.svg'; import SpinnerIcon from '../../icons/spinner.svg'; import Cookies from 'js-cookie'; import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; +import { httpGet } from '../../lib/http'; type GitHubButtonProps = {}; @@ -26,42 +27,40 @@ export function GitHubButton(props: GitHubButtonProps) { } setIsLoading(true); - fetch( + httpGet<{ token: string }>( `${import.meta.env.PUBLIC_API_URL}/v1-github-callback${ window.location.search - }`, - { - method: 'GET', - credentials: 'include', - } + }` ) - .then((res) => res.json()) - .then((data: any) => { - if (!data.token) { - setError('Something went wrong. Please try again later.'); + .then(({ response, error }) => { + if (!response?.token) { + const errMessage = error?.message || 'Something went wrong.'; + setError(errMessage); setIsLoading(false); - } else { - let redirectUrl = '/'; - const gitHubRedirectAt = localStorage.getItem(GITHUB_REDIRECT_AT); - const lastPageBeforeGithub = localStorage.getItem(GITHUB_LAST_PAGE); - - // If the social redirect is there and less than 30 seconds old - // redirect to the page that user was on before they clicked the github login button - if (gitHubRedirectAt && lastPageBeforeGithub) { - const socialRedirectAtTime = parseInt(gitHubRedirectAt, 10); - const now = Date.now(); - const timeSinceRedirect = now - socialRedirectAtTime; - - if (timeSinceRedirect < 30 * 1000) { - redirectUrl = lastPageBeforeGithub; - } - } - localStorage.removeItem(GITHUB_REDIRECT_AT); - localStorage.removeItem(GITHUB_LAST_PAGE); - Cookies.set(TOKEN_COOKIE_NAME, data.token); - window.location.href = redirectUrl; + return; + } + + let redirectUrl = '/'; + const gitHubRedirectAt = localStorage.getItem(GITHUB_REDIRECT_AT); + const lastPageBeforeGithub = localStorage.getItem(GITHUB_LAST_PAGE); + + // If the social redirect is there and less than 30 seconds old + // redirect to the page that user was on before they clicked the github login button + if (gitHubRedirectAt && lastPageBeforeGithub) { + const socialRedirectAtTime = parseInt(gitHubRedirectAt, 10); + const now = Date.now(); + const timeSinceRedirect = now - socialRedirectAtTime; + + if (timeSinceRedirect < 30 * 1000) { + redirectUrl = lastPageBeforeGithub; + } } + + localStorage.removeItem(GITHUB_REDIRECT_AT); + localStorage.removeItem(GITHUB_LAST_PAGE); + Cookies.set(TOKEN_COOKIE_NAME, response.token); + window.location.href = redirectUrl; }) .catch((err) => { setError('Something went wrong. Please try again later.'); @@ -69,33 +68,30 @@ export function GitHubButton(props: GitHubButtonProps) { }); }, []); - const handleClick = () => { + const handleClick = async () => { setIsLoading(true); - fetch(`${import.meta.env.PUBLIC_API_URL}/v1-github-login`, { - credentials: 'include', - redirect: 'follow', - }) - .then((res) => res.json()) - .then((data: any) => { - // @todo proper typing for API response - if (data.loginUrl) { - // For non authentication pages, we want to redirect back to the page - // the user was on before they clicked the social login button - if (!['/login', '/signup'].includes(window.location.pathname)) { - localStorage.setItem(GITHUB_REDIRECT_AT, Date.now().toString()); - localStorage.setItem(GITHUB_LAST_PAGE, window.location.pathname); - } - window.location.href = data.loginUrl; - } else { - setError('Something went wrong. Please try again later.'); - setIsLoading(false); - } - }) - .catch((err) => { - setError('Something went wrong. Please try again later.'); - setIsLoading(false); - }); + const { response, error } = await httpGet<{ loginUrl: string }>( + `${import.meta.env.PUBLIC_API_URL}/v1-github-login` + ); + + if (error || !response?.loginUrl) { + setError( + error?.message || 'Something went wrong. Please try again later.' + ); + + setIsLoading(false); + return; + } + + // For non authentication pages, we want to redirect back to the page + // the user was on before they clicked the social login button + if (!['/login', '/signup'].includes(window.location.pathname)) { + localStorage.setItem(GITHUB_REDIRECT_AT, Date.now().toString()); + localStorage.setItem(GITHUB_LAST_PAGE, window.location.pathname); + } + + window.location.href = response.loginUrl; }; return ( diff --git a/src/components/AuthenticationFlow/GoogleButton.tsx b/src/components/AuthenticationFlow/GoogleButton.tsx index df45cc808..a06e531c2 100644 --- a/src/components/AuthenticationFlow/GoogleButton.tsx +++ b/src/components/AuthenticationFlow/GoogleButton.tsx @@ -3,6 +3,7 @@ import Cookies from 'js-cookie'; import GoogleIcon from '../../icons/google.svg'; import SpinnerIcon from '../../icons/spinner.svg'; import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; +import { httpGet } from '../../lib/http'; type GoogleButtonProps = {}; @@ -25,42 +26,39 @@ export function GoogleButton(props: GoogleButtonProps) { } setIsLoading(true); - fetch( + httpGet<{ token: string }>( `${import.meta.env.PUBLIC_API_URL}/v1-google-callback${ window.location.search - }`, - { - method: 'GET', - credentials: 'include', - } + }` ) - .then((res) => res.json()) - .then((data: any) => { - if (!data.token) { - setError('Something went wrong. Please try again later.'); + .then(({ response, error }) => { + if (!response?.token) { + setError(error?.message || 'Something went wrong.'); setIsLoading(false); - } else { - let redirectUrl = '/'; - const googleRedirectAt = localStorage.getItem(GOOGLE_REDIRECT_AT); - const lastPageBeforeGoogle = localStorage.getItem(GOOGLE_LAST_PAGE); - - // If the social redirect is there and less than 30 seconds old - // redirect to the page that user was on before they clicked the github login button - if (googleRedirectAt && lastPageBeforeGoogle) { - const socialRedirectAtTime = parseInt(googleRedirectAt, 10); - const now = Date.now(); - const timeSinceRedirect = now - socialRedirectAtTime; - - if (timeSinceRedirect < 30 * 1000) { - redirectUrl = lastPageBeforeGoogle; - } - } - localStorage.removeItem(GOOGLE_REDIRECT_AT); - localStorage.removeItem(GOOGLE_LAST_PAGE); - Cookies.set(TOKEN_COOKIE_NAME, data.token); - window.location.href = redirectUrl; + return; } + + let redirectUrl = '/'; + const googleRedirectAt = localStorage.getItem(GOOGLE_REDIRECT_AT); + const lastPageBeforeGoogle = localStorage.getItem(GOOGLE_LAST_PAGE); + + // If the social redirect is there and less than 30 seconds old + // redirect to the page that user was on before they clicked the github login button + if (googleRedirectAt && lastPageBeforeGoogle) { + const socialRedirectAtTime = parseInt(googleRedirectAt, 10); + const now = Date.now(); + const timeSinceRedirect = now - socialRedirectAtTime; + + if (timeSinceRedirect < 30 * 1000) { + redirectUrl = lastPageBeforeGoogle; + } + } + + localStorage.removeItem(GOOGLE_REDIRECT_AT); + localStorage.removeItem(GOOGLE_LAST_PAGE); + Cookies.set(TOKEN_COOKIE_NAME, response.token); + window.location.href = redirectUrl; }) .catch((err) => { setError('Something went wrong. Please try again later.'); @@ -70,26 +68,25 @@ export function GoogleButton(props: GoogleButtonProps) { const handleClick = () => { setIsLoading(true); - fetch(`${import.meta.env.PUBLIC_API_URL}/v1-google-login`, { - credentials: 'include', - redirect: 'follow', - }) - .then((res) => res.json()) - .then((data: any) => { - // @todo proper typing for API response - if (data.loginUrl) { - // For non authentication pages, we want to redirect back to the page - // the user was on before they clicked the social login button - if (!['/login', '/signup'].includes(window.location.pathname)) { - localStorage.setItem(GOOGLE_REDIRECT_AT, Date.now().toString()); - localStorage.setItem(GOOGLE_LAST_PAGE, window.location.pathname); - } - - window.location.href = data.loginUrl; - } else { - setError('Something went wrong. Please try again later.'); + httpGet<{ loginUrl: string }>( + `${import.meta.env.PUBLIC_API_URL}/v1-google-login` + ) + .then(({ response, error }) => { + if (!response?.loginUrl) { + setError(error?.message || 'Something went wrong.'); setIsLoading(false); + + return; } + + // For non authentication pages, we want to redirect back to the page + // the user was on before they clicked the social login button + if (!['/login', '/signup'].includes(window.location.pathname)) { + localStorage.setItem(GOOGLE_REDIRECT_AT, Date.now().toString()); + localStorage.setItem(GOOGLE_LAST_PAGE, window.location.pathname); + } + + window.location.href = response.loginUrl; }) .catch((err) => { setError('Something went wrong. Please try again later.'); diff --git a/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx b/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx index 0979152ea..2ea766d31 100644 --- a/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx +++ b/src/components/AuthenticationFlow/TriggerVerifyAccount.tsx @@ -3,7 +3,8 @@ import ErrorIcon from '../../icons/error.svg'; import { useEffect, useState } from 'preact/hooks'; import Cookies from 'js-cookie'; -import {TOKEN_COOKIE_NAME} from "../../lib/jwt"; +import { TOKEN_COOKIE_NAME } from '../../lib/jwt'; +import { httpPost } from '../../lib/http'; export function TriggerVerifyAccount() { const [isLoading, setIsLoading] = useState(true); @@ -12,22 +13,21 @@ export function TriggerVerifyAccount() { const triggerVerify = (code: string) => { setIsLoading(true); - fetch(`${import.meta.env.PUBLIC_API_URL}/v1-verify-account`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ + httpPost<{ token: string }>( + `${import.meta.env.PUBLIC_API_URL}/v1-verify-account`, + { code, - }), - }) - .then((res) => res.json()) - .then((data: any) => { - if (!data.token) { - throw new Error('Something went wrong. Please try again..'); + } + ) + .then(({ response, error }) => { + if (!response?.token) { + setError(error?.message || 'Something went wrong. Please try again.'); + setIsLoading(false); + + return; } - Cookies.set(TOKEN_COOKIE_NAME, data.token); + Cookies.set(TOKEN_COOKIE_NAME, response.token); window.location.href = '/'; }) .catch((err) => { diff --git a/src/components/AuthenticationFlow/VerificationEmailMessage.tsx b/src/components/AuthenticationFlow/VerificationEmailMessage.tsx index 1cb4d6f26..e219d19e4 100644 --- a/src/components/AuthenticationFlow/VerificationEmailMessage.tsx +++ b/src/components/AuthenticationFlow/VerificationEmailMessage.tsx @@ -1,5 +1,6 @@ import VerifyLetterIcon from '../../icons/verify-letter.svg'; import { useEffect, useState } from 'preact/hooks'; +import { httpPost } from '../../lib/http'; export function VerificationEmailMessage() { const [email, setEmail] = useState('..'); @@ -14,18 +15,15 @@ export function VerificationEmailMessage() { }, []); const resendVerificationEmail = () => { - fetch(`${import.meta.env.PUBLIC_API_URL}/v1-send-verification-email`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify({ - email, - }), + httpPost(`${import.meta.env.PUBLIC_API_URL}/v1-send-verification-email`, { + email, }) - .then((res) => { - if (!res.ok) { - throw new Error('Something went wrong. Please try again later.'); + .then(({ response, error }) => { + if (error) { + setIsEmailResent(false); + setError(error?.message || 'Something went wrong.'); + setIsLoading(false); + return; } setIsEmailResent(true); @@ -49,9 +47,10 @@ export function VerificationEmailMessage() {

- We have sent you an email at {email}. - Please click the link to verify your account. This link will expire - shortly, so please verify soon! + We have sent you an email at{' '} + {email}. Please click the link to + verify your account. This link will expire shortly, so please verify + soon!


@@ -77,7 +76,9 @@ export function VerificationEmailMessage() { )} - {isEmailResent &&

Verification email has been sent!

} + {isEmailResent && ( +

Verification email has been sent!

+ )}
); diff --git a/src/hooks/use-auth.ts b/src/hooks/use-auth.ts deleted file mode 100644 index 891903de2..000000000 --- a/src/hooks/use-auth.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { useEffect, useState } from 'preact/hooks'; -import {TokenPayload, decodeToken, TOKEN_COOKIE_NAME} from '../lib/jwt'; -import Cookies from 'js-cookie'; - -export const useAuth = () => { - const [user, setUser] = useState(null); - const [isLoading, setIsLoading] = useState(true); - - useEffect(() => { - const token = Cookies.get(TOKEN_COOKIE_NAME); - const payload = token ? decodeToken(token) : null; - - setUser(payload); - setIsLoading(false); - }, []); - - return { user, isLoading }; -};