2023-11-14 15:10:00 +00:00
|
|
|
import type { AuthModalSearchParams } from './types'
|
2022-10-25 16:25:42 +00:00
|
|
|
import type { JSX } from 'solid-js'
|
2023-11-14 15:10:00 +00:00
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
import { clsx } from 'clsx'
|
2023-11-14 15:10:00 +00:00
|
|
|
import { Show, createSignal } from 'solid-js'
|
|
|
|
|
|
|
|
import { useLocalize } from '../../../context/localize'
|
2023-12-15 13:45:34 +00:00
|
|
|
import { useSession } from '../../../context/session'
|
2023-11-14 15:10:00 +00:00
|
|
|
import { checkEmail, useEmailChecks } from '../../../stores/emailChecks'
|
2022-10-25 16:25:42 +00:00
|
|
|
import { useRouter } from '../../../stores/router'
|
|
|
|
import { hideModal } from '../../../stores/ui'
|
2023-07-02 05:08:42 +00:00
|
|
|
import { validateEmail } from '../../../utils/validateEmail'
|
2022-10-25 16:25:42 +00:00
|
|
|
|
2023-11-14 15:10:00 +00:00
|
|
|
import { AuthModalHeader } from './AuthModalHeader'
|
2023-12-21 10:02:28 +00:00
|
|
|
import { PasswordField } from './PasswordField'
|
2023-11-14 15:10:00 +00:00
|
|
|
import { email, setEmail } from './sharedLogic'
|
|
|
|
import { SocialProviders } from './SocialProviders'
|
|
|
|
|
|
|
|
import styles from './AuthModal.module.scss'
|
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
type FormFields = {
|
2023-08-13 13:36:18 +00:00
|
|
|
fullName: string
|
2022-10-25 16:25:42 +00:00
|
|
|
email: string
|
|
|
|
password: string
|
|
|
|
}
|
|
|
|
|
|
|
|
type ValidationErrors = Partial<Record<keyof FormFields, string | JSX.Element>>
|
|
|
|
|
2023-08-28 11:48:54 +00:00
|
|
|
const handleEmailInput = (newEmail: string) => {
|
2024-01-13 14:22:04 +00:00
|
|
|
setEmail(newEmail.toLowerCase())
|
2023-08-28 11:48:54 +00:00
|
|
|
}
|
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
export const RegisterForm = () => {
|
2023-12-20 08:07:57 +00:00
|
|
|
const { changeSearchParams } = useRouter<AuthModalSearchParams>()
|
2023-02-17 09:21:02 +00:00
|
|
|
const { t } = useLocalize()
|
2022-11-13 19:35:57 +00:00
|
|
|
const { emailChecks } = useEmailChecks()
|
2023-12-14 11:49:55 +00:00
|
|
|
const {
|
2023-12-24 08:16:41 +00:00
|
|
|
actions: { signUp },
|
2023-12-14 11:49:55 +00:00
|
|
|
} = useSession()
|
2022-10-25 16:25:42 +00:00
|
|
|
const [submitError, setSubmitError] = createSignal('')
|
2023-08-13 13:36:18 +00:00
|
|
|
const [fullName, setFullName] = createSignal('')
|
2022-10-25 16:25:42 +00:00
|
|
|
const [password, setPassword] = createSignal('')
|
|
|
|
const [isSubmitting, setIsSubmitting] = createSignal(false)
|
|
|
|
const [isSuccess, setIsSuccess] = createSignal(false)
|
|
|
|
const [validationErrors, setValidationErrors] = createSignal<ValidationErrors>({})
|
2023-12-21 10:02:28 +00:00
|
|
|
const [passwordError, setPasswordError] = createSignal<string>()
|
2022-10-25 16:25:42 +00:00
|
|
|
|
2023-08-13 13:36:18 +00:00
|
|
|
const authFormRef: { current: HTMLFormElement } = { current: null }
|
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
const handleEmailBlur = () => {
|
2023-07-02 05:08:42 +00:00
|
|
|
if (validateEmail(email())) {
|
2022-10-25 16:25:42 +00:00
|
|
|
checkEmail(email())
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-21 10:02:28 +00:00
|
|
|
const handleNameInput = (newName: string) => {
|
|
|
|
setFullName(newName)
|
2022-10-25 16:25:42 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
const handleSubmit = async (event: Event) => {
|
|
|
|
event.preventDefault()
|
|
|
|
|
2023-12-21 10:02:28 +00:00
|
|
|
if (passwordError()) {
|
|
|
|
setValidationErrors((errors) => ({ ...errors, password: passwordError() }))
|
2023-08-21 11:11:18 +00:00
|
|
|
} else {
|
|
|
|
setValidationErrors(({ password: _notNeeded, ...rest }) => rest)
|
|
|
|
}
|
|
|
|
setValidationErrors(({ email: _notNeeded, ...rest }) => rest)
|
|
|
|
setValidationErrors(({ fullName: _notNeeded, ...rest }) => rest)
|
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
setSubmitError('')
|
|
|
|
|
|
|
|
const newValidationErrors: ValidationErrors = {}
|
|
|
|
|
2023-08-13 13:36:18 +00:00
|
|
|
const cleanName = fullName().trim()
|
2022-12-01 18:45:35 +00:00
|
|
|
const cleanEmail = email().trim()
|
2022-11-13 19:35:57 +00:00
|
|
|
|
2022-12-01 18:45:35 +00:00
|
|
|
if (!cleanName) {
|
2023-08-13 13:36:18 +00:00
|
|
|
newValidationErrors.fullName = t('Please enter a name to sign your comments and publication')
|
2022-10-25 16:25:42 +00:00
|
|
|
}
|
|
|
|
|
2022-12-01 18:45:35 +00:00
|
|
|
if (!cleanEmail) {
|
2022-10-25 16:25:42 +00:00
|
|
|
newValidationErrors.email = t('Please enter email')
|
2023-07-02 05:08:42 +00:00
|
|
|
} else if (!validateEmail(email())) {
|
2022-10-25 16:25:42 +00:00
|
|
|
newValidationErrors.email = t('Invalid email')
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!password()) {
|
|
|
|
newValidationErrors.password = t('Please enter password')
|
|
|
|
}
|
|
|
|
|
|
|
|
setValidationErrors(newValidationErrors)
|
|
|
|
|
2022-12-01 18:45:35 +00:00
|
|
|
const emailCheckResult = await checkEmail(cleanEmail)
|
2022-10-25 16:25:42 +00:00
|
|
|
|
|
|
|
const isValid = Object.keys(newValidationErrors).length === 0 && !emailCheckResult
|
|
|
|
|
|
|
|
if (!isValid) {
|
2023-08-13 13:36:18 +00:00
|
|
|
authFormRef.current
|
|
|
|
.querySelector<HTMLInputElement>(`input[name="${Object.keys(newValidationErrors)[0]}"]`)
|
|
|
|
.focus()
|
|
|
|
|
2022-10-25 16:25:42 +00:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
setIsSubmitting(true)
|
|
|
|
try {
|
2023-12-24 08:16:41 +00:00
|
|
|
const opts = {
|
2023-11-28 18:04:51 +00:00
|
|
|
given_name: cleanName,
|
2022-12-01 18:45:35 +00:00
|
|
|
email: cleanEmail,
|
2023-11-14 15:10:00 +00:00
|
|
|
password: password(),
|
2023-11-28 18:04:51 +00:00
|
|
|
confirm_password: password(),
|
2023-12-03 10:22:42 +00:00
|
|
|
redirect_uri: window.location.origin,
|
2023-12-24 08:16:41 +00:00
|
|
|
}
|
2024-02-01 20:34:53 +00:00
|
|
|
const { errors } = await signUp(opts)
|
|
|
|
if (errors && errors.some((error) => error.message.includes('has already signed up'))) {
|
|
|
|
setValidationErrors((prev) => ({
|
|
|
|
...prev,
|
|
|
|
email: (
|
|
|
|
<>
|
|
|
|
{t('User with this email already exists')},{' '}
|
|
|
|
<span
|
|
|
|
class={'link'}
|
|
|
|
onClick={() =>
|
|
|
|
changeSearchParams({
|
|
|
|
mode: 'login',
|
|
|
|
})
|
|
|
|
}
|
|
|
|
>
|
|
|
|
{t('sign in')}
|
|
|
|
</span>
|
|
|
|
</>
|
|
|
|
),
|
|
|
|
}))
|
|
|
|
}
|
2022-10-25 16:25:42 +00:00
|
|
|
setIsSuccess(true)
|
|
|
|
} catch (error) {
|
2023-12-24 08:16:41 +00:00
|
|
|
console.error(error)
|
2022-10-25 16:25:42 +00:00
|
|
|
} finally {
|
|
|
|
setIsSubmitting(false)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return (
|
|
|
|
<>
|
|
|
|
<Show when={!isSuccess()}>
|
2023-08-13 13:36:18 +00:00
|
|
|
<form onSubmit={handleSubmit} class={styles.authForm} ref={(el) => (authFormRef.current = el)}>
|
2023-05-18 20:02:19 +00:00
|
|
|
<div>
|
2023-07-30 12:31:54 +00:00
|
|
|
<AuthModalHeader modalType="register" />
|
2023-05-18 20:02:19 +00:00
|
|
|
<Show when={submitError()}>
|
|
|
|
<div class={styles.authInfo}>
|
2024-01-27 06:21:48 +00:00
|
|
|
<div class={styles.warn}>{submitError()}</div>
|
2023-05-18 20:02:19 +00:00
|
|
|
</div>
|
|
|
|
</Show>
|
2023-08-13 13:36:18 +00:00
|
|
|
<div
|
|
|
|
class={clsx('pretty-form__item', {
|
2023-11-14 15:10:00 +00:00
|
|
|
'pretty-form__item--error': validationErrors().fullName,
|
2023-08-13 13:36:18 +00:00
|
|
|
})}
|
|
|
|
>
|
2023-05-18 20:02:19 +00:00
|
|
|
<input
|
|
|
|
name="fullName"
|
|
|
|
type="text"
|
|
|
|
placeholder={t('Full name')}
|
|
|
|
autocomplete=""
|
|
|
|
onInput={(event) => handleNameInput(event.currentTarget.value)}
|
|
|
|
/>
|
2023-08-13 13:36:18 +00:00
|
|
|
<label for="name">{t('Full name')}</label>
|
2023-08-30 21:30:15 +00:00
|
|
|
<Show when={validationErrors().fullName}>
|
|
|
|
<div class={styles.validationError}>{validationErrors().fullName}</div>
|
|
|
|
</Show>
|
2022-10-25 16:25:42 +00:00
|
|
|
</div>
|
2023-08-30 21:30:15 +00:00
|
|
|
|
2023-08-13 13:36:18 +00:00
|
|
|
<div
|
|
|
|
class={clsx('pretty-form__item', {
|
2023-11-14 15:10:00 +00:00
|
|
|
'pretty-form__item--error': validationErrors().email,
|
2023-08-13 13:36:18 +00:00
|
|
|
})}
|
|
|
|
>
|
2023-05-18 20:02:19 +00:00
|
|
|
<input
|
|
|
|
id="email"
|
|
|
|
name="email"
|
|
|
|
autocomplete="email"
|
|
|
|
type="email"
|
|
|
|
value={email()}
|
|
|
|
placeholder={t('Email')}
|
|
|
|
onInput={(event) => handleEmailInput(event.currentTarget.value)}
|
|
|
|
onBlur={handleEmailBlur}
|
|
|
|
/>
|
|
|
|
<label for="email">{t('Email')}</label>
|
2023-08-30 21:30:15 +00:00
|
|
|
<Show when={validationErrors().email}>
|
|
|
|
<div class={styles.validationError}>{validationErrors().email}</div>
|
|
|
|
</Show>
|
2023-09-04 21:50:48 +00:00
|
|
|
<Show when={emailChecks()[email()]}>
|
|
|
|
<div class={styles.validationError}>
|
|
|
|
{t("This email is already taken. If it's you")},{' '}
|
2023-12-21 10:02:28 +00:00
|
|
|
<span class="link" onClick={() => changeSearchParams({ mode: 'login' })}>
|
2023-09-04 21:50:48 +00:00
|
|
|
{t('enter')}
|
2023-12-21 10:02:28 +00:00
|
|
|
</span>
|
2023-09-04 21:50:48 +00:00
|
|
|
</div>
|
|
|
|
</Show>
|
2022-10-25 16:25:42 +00:00
|
|
|
</div>
|
2023-08-30 21:30:15 +00:00
|
|
|
|
2023-12-21 10:02:28 +00:00
|
|
|
<PasswordField
|
|
|
|
errorMessage={(err) => setPasswordError(err)}
|
|
|
|
onInput={(value) => setPassword(value)}
|
|
|
|
/>
|
2023-05-18 20:02:19 +00:00
|
|
|
|
|
|
|
<div>
|
|
|
|
<button class={clsx('button', styles.submitButton)} disabled={isSubmitting()} type="submit">
|
|
|
|
{isSubmitting() ? '...' : t('Join')}
|
|
|
|
</button>
|
2023-05-11 17:20:40 +00:00
|
|
|
</div>
|
2022-10-25 16:25:42 +00:00
|
|
|
</div>
|
|
|
|
|
2023-05-18 20:02:19 +00:00
|
|
|
<div>
|
|
|
|
<SocialProviders />
|
2022-10-25 16:25:42 +00:00
|
|
|
|
2023-05-18 20:02:19 +00:00
|
|
|
<div class={styles.authControl}>
|
2023-09-29 12:48:58 +00:00
|
|
|
<span
|
|
|
|
class={styles.authLink}
|
|
|
|
onClick={() =>
|
2023-12-20 08:07:57 +00:00
|
|
|
changeSearchParams({
|
2023-11-14 15:10:00 +00:00
|
|
|
mode: 'login',
|
2023-09-29 12:48:58 +00:00
|
|
|
})
|
|
|
|
}
|
|
|
|
>
|
2023-05-18 20:02:19 +00:00
|
|
|
{t('I have an account')}
|
|
|
|
</span>
|
|
|
|
</div>
|
2022-10-25 16:25:42 +00:00
|
|
|
</div>
|
|
|
|
</form>
|
|
|
|
</Show>
|
|
|
|
<Show when={isSuccess()}>
|
|
|
|
<div class={styles.title}>{t('Almost done! Check your email.')}</div>
|
|
|
|
<div class={styles.text}>{t("We've sent you a message with a link to enter our website.")}</div>
|
|
|
|
<div>
|
|
|
|
<button class={clsx('button', styles.submitButton)} onClick={() => hideModal()}>
|
|
|
|
{t('Back to main page')}
|
|
|
|
</button>
|
|
|
|
</div>
|
|
|
|
</Show>
|
|
|
|
</>
|
|
|
|
)
|
|
|
|
}
|