// Utils
import { horFlexClasses, horFlexClassesNoSm, capitalize } from '@freesewing/utils'
// Context
import { LoadingStatusContext } from '@freesewing/react/context/LoadingStatus'
// Hooks
import React, { useState, useEffect, useContext } from 'react'
import { useAccount } from '@freesewing/react/hooks/useAccount'
import { useBackend } from '@freesewing/react/hooks/useBackend'
// Components
import { Link } from '@freesewing/react/components/Link'
import {
EmailIcon,
KeyIcon,
LockIcon,
WarningIcon,
GoogleIcon,
GitHubIcon,
FreeSewingIcon,
UserIcon,
} from '@freesewing/react/components/Icon'
import { MfaInput, StringInput, PasswordInput } from '@freesewing/react/components/Input'
import { H1, H2, H3, H4 } from '@freesewing/react/components/Heading'
/*
* This SignIn component holds the entire sign-in form
*
* @param {object} props - All React props
* @param {function} props.onSuccess - Optional: A method to run when the sign in is successful
*/
export const SignIn = ({ onSuccess = false }) => {
const { setAccount, setToken, seenUser, setSeenUser } = useAccount()
const backend = useBackend()
const { setLoadingStatus } = useContext(LoadingStatusContext)
const [username, setUsername] = useState('')
const [password, setPassword] = useState('')
const [magicLink, setMagicLink] = useState(true)
const [signInFailed, setSignInFailed] = useState(false)
const [magicLinkSent, setMagicLinkSent] = useState(false)
const [seenBefore, setSeenBefore] = useState(false)
const [mfa, setMfa] = useState(false)
const [mfaCode, setMfaCode] = useState('')
useEffect(() => {
if (typeof window !== 'undefined' && signInFailed) {
window.setTimeout(() => setSignInFailed(false), 1750)
}
}, [signInFailed])
// Avoid SSR rendering mismatch by setting this in effect
useEffect(() => {
if (seenUser) {
setSeenBefore(seenUser)
setUsername(seenUser)
} else {
setSeenBefore(false)
setUsername('')
}
}, [seenUser])
const triggerSubmit = (evt) => {
if (evt.key === 'Enter') signinHandler(evt)
}
const signinHandler = async (evt) => {
evt.preventDefault()
setLoadingStatus([true, 'Contacting FreeSewing backend'])
const result = magicLink
? await backend.signIn({ username, password: false })
: await backend.signIn({ username, password, token: mfaCode })
const [status, body] = Array.isArray(result) ? result : [false, false]
if (!status) {
setSignInFailed('Unexpected error when attempting sign-in')
return setLoadingStatus([
true,
'Unexpected error when attempting sign-in. Please report this.',
true,
false,
])
}
// Sign-in succeeded
if (status === 200) {
if (magicLink) {
setLoadingStatus([true, 'Email sent', true, true])
setMagicLinkSent(true)
} else {
setAccount(body.account)
setToken(body.token)
setSeenUser(body.account.username)
setLoadingStatus([true, `Welcome back ${body.account.username}`, true, true])
// Call the onSuccess handler
if (typeof onSuccess === 'function') onSuccess(body)
}
}
// Sign-in failed
if (status === 401) {
const msg = magicLink ? 'Unable to find this user' : 'Sign-In failed'
setSignInFailed(msg)
setLoadingStatus([true, msg, true, false])
}
// Bad request
if (status === 400) {
let msg
if (result.data.error === 'usernameMissing') msg = 'Please provide your username'
else if (result.data.error === 'passwordMissing') msg = 'Please provide your password'
setSignInFailed(msg)
setLoadingStatus([true, msg, true, false])
}
// MFA active
if (status === 403 && body.error === 'mfaTokenRequired') {
setMfa(true)
setLoadingStatus([
true,
'Please provide a one-time MFA code, or a backup scratch code',
true,
true,
])
}
}
const initOauth = async (provider) => {
setLoadingStatus([true, 'Contacting the FreeSewing backend'])
const result = await backend.oauthInit({ provider, language: 'en' })
if (result.success) {
setLoadingStatus([true, `Contacting ${capitalize(provider)}`])
window.location.href = result.data.authUrl
}
}
const btnClasses = `tw-daisy-btn tw-capitalize tw-w-full tw-mt-4 ${
signInFailed ? 'tw-daisy-btn-warning' : 'tw-daisy-btn-primary'
} tw-transition-colors tw-ease-in-out tw-duration-300 ${horFlexClassesNoSm}`
const noBueno = (
<>
Go check your inbox for an email from FreeSewing.org
Click the sign-in link in that email to sign in to your FreeSewing account.
Please provide a one-time MFA code, or a backup scratch code
Email Sent
MFA Code
{seenBefore ? `Welcome back ${seenUser}` : 'Welcome'}
Sign in to FreeSewing
{!seenBefore && (