import type { AuthModalSearchParams } from './types' import { clsx } from 'clsx' import { createSignal, Show } from 'solid-js' import { useAuthorizer } from '../../../context/authorizer' import { useLocalize } from '../../../context/localize' import { useSession } from '../../../context/session' import { useSnackbar } from '../../../context/snackbar' import { ApiError } from '../../../graphql/error' import { useRouter } from '../../../stores/router' import { hideModal } from '../../../stores/ui' import { validateEmail } from '../../../utils/validateEmail' import { Icon } from '../../_shared/Icon' import { AuthModalHeader } from './AuthModalHeader' import { email, setEmail } from './sharedLogic' import { SocialProviders } from './SocialProviders' import styles from './AuthModal.module.scss' type FormFields = { email: string password: string } type ValidationErrors = Partial> export const LoginForm = () => { const { t } = useLocalize() const [submitError, setSubmitError] = createSignal('') const [isSubmitting, setIsSubmitting] = createSignal(false) const [validationErrors, setValidationErrors] = createSignal({}) // TODO: better solution for interactive error messages const [isEmailNotConfirmed, setIsEmailNotConfirmed] = createSignal(false) const [isLinkSent, setIsLinkSent] = createSignal(false) const [showPassword, setShowPassword] = createSignal(false) const authFormRef: { current: HTMLFormElement } = { current: null } const { actions: { showSnackbar }, } = useSnackbar() const { actions: { signIn }, } = useSession() const { changeSearchParam } = useRouter() const [password, setPassword] = createSignal('') const handleEmailInput = (newEmail: string) => { setValidationErrors(({ email: _notNeeded, ...rest }) => rest) setEmail(newEmail) } const handlePasswordInput = (newPassword: string) => { setValidationErrors(({ password: _notNeeded, ...rest }) => rest) setPassword(newPassword) } const handleSendLinkAgainClick = async (event: Event) => { event.preventDefault() setIsLinkSent(true) setIsEmailNotConfirmed(false) setSubmitError('') const [{ token }, { authorizer }] = useAuthorizer() const result = await authorizer().verifyEmail({ token: token.id_token }) if (!result) setSubmitError('cant sign send link') // TODO: } const handleSubmit = async (event: Event) => { event.preventDefault() setIsLinkSent(false) setIsEmailNotConfirmed(false) setSubmitError('') const newValidationErrors: ValidationErrors = {} if (!email()) { newValidationErrors.email = t('Please enter email') } else if (!validateEmail(email())) { newValidationErrors.email = t('Invalid email') } if (!password()) { newValidationErrors.password = t('Please enter password') } if (Object.keys(newValidationErrors).length > 0) { setValidationErrors(newValidationErrors) authFormRef.current .querySelector(`input[name="${Object.keys(newValidationErrors)[0]}"]`) .focus() return } setIsSubmitting(true) try { await signIn({ email: email(), password: password() }) hideModal() showSnackbar({ body: t('Welcome!') }) } catch (error) { if (error instanceof ApiError) { if (error.code === 'email_not_confirmed') { setSubmitError(t('Please, confirm email')) setIsEmailNotConfirmed(true) return } if (error.code === 'user_not_found') { setSubmitError(t('Something went wrong, check email and password')) return } } setSubmitError(error.message) } finally { setIsSubmitting(false) } } return (
(authFormRef.current = el)}>
{submitError()}
{t('Send link again')}
{t('Link sent, check your email')}
handleEmailInput(event.currentTarget.value)} />
{validationErrors().email}
handlePasswordInput(event.currentTarget.value)} />
{validationErrors().password}
changeSearchParam({ mode: 'forgot-password', }) } > {t('Forgot password?')}
changeSearchParam({ mode: 'register', }) } > {t('I have no account yet')}
) }