webapp/src/components/Nav/AuthModal.tsx

327 lines
12 KiB
TypeScript
Raw Normal View History

2022-09-09 14:13:50 +00:00
import { Show } from 'solid-js/web'
2022-09-22 09:37:49 +00:00
import { Icon } from './Icon'
2022-09-09 11:53:35 +00:00
import { createEffect, createSignal, onMount } from 'solid-js'
import './AuthModal.scss'
import { Form } from 'solid-js-form'
import { t } from '../../utils/intl'
import { hideModal, useModalStore } from '../../stores/ui'
import { useStore } from '@nanostores/solid'
2022-09-13 09:59:04 +00:00
import { session as sessionstore, signIn } from '../../stores/auth'
2022-09-09 11:53:35 +00:00
import { apiClient } from '../../utils/apiClient'
import { useValidator } from '../../utils/validators'
2022-09-14 11:05:48 +00:00
import { baseUrl } from '../../graphql/publicGraphQLClient'
2022-09-09 11:53:35 +00:00
type AuthMode = 'sign-in' | 'sign-up' | 'forget' | 'reset' | 'resend' | 'password'
const statuses: { [key: string]: string } = {
'email not found': 'No such account, please try to register',
'invalid password': 'Invalid password',
'invalid code': 'Invalid code',
'unknown error': 'Unknown error'
}
const titles = {
'sign-up': t('Create account'),
'sign-in': t('Enter the Discours'),
forget: t('Forget password?'),
reset: t('Please, confirm your email to finish'),
resend: t('Resend code'),
password: t('Enter your new password')
}
2022-09-22 09:37:49 +00:00
// const isProperEmail = (email) => email && email.length > 5 && email.includes('@') && email.includes('.')
2022-09-09 11:53:35 +00:00
// FIXME !!!
// eslint-disable-next-line sonarjs/cognitive-complexity
export default (props: { code?: string; mode?: string }) => {
const session = useStore(sessionstore)
const [handshaking] = createSignal(false)
const { getModal } = useModalStore()
const [authError, setError] = createSignal('')
const [mode, setMode] = createSignal<AuthMode>('sign-in')
const [validation, setValidation] = createSignal({})
const [initial, setInitial] = createSignal({})
let emailElement: HTMLInputElement | undefined
let pass2Element: HTMLInputElement | undefined
let passElement: HTMLInputElement | undefined
let codeElement: HTMLInputElement | undefined
// 3rd party providier auth handler
const oauth = (provider: string): void => {
const popup = window.open(`${baseUrl}/oauth/${provider}`, provider, 'width=740, height=420')
popup?.focus()
hideModal()
}
// FIXME: restore logic
// const usedEmails = {}
// const checkEmailAsync = async (email: string) => {
// const handleChecked = (x: boolean) => {
// if (x && mode() === 'sign-up') setError(t('We know you, please try to sign in'))
// if (!x && mode() === 'sign-in') setError(t('No such account, please try to register'))
// usedEmails[email] = x
// }
// if (email in usedEmails) {
// handleChecked(usedEmails[email])
// } else if (isProperEmail(email)) {
// const { error, data } = await apiClient.q(authCheck, { email }, true)
// if (error) setError(error.message)
// if (data) handleChecked(data.isEmailUsed)
// }
// }
// let checkEmailTimeout
// createEffect(() => {
// const email = emailElement?.value
// if (isProperEmail(email)) {
// if (checkEmailTimeout) clearTimeout(checkEmailTimeout)
// checkEmailTimeout = setTimeout(checkEmailAsync, 3000) // after 3 secs
// }
// }, [emailElement?.value])
// switching initial values and validatiors
const setupValidators = () => {
const [vs, ini] = useValidator(mode())
setValidation(vs)
setInitial(ini)
}
onMount(setupValidators)
// local auth handler
const localAuth = async () => {
console.log('[auth] native account processing')
switch (mode()) {
case 'sign-in':
signIn({ email: emailElement?.value, password: passElement?.value })
break
case 'sign-up':
if (pass2Element?.value !== passElement?.value) {
setError(t('Passwords are not equal'))
} else {
// FIXME use store actions
2022-09-17 19:15:55 +00:00
const r = await apiClient.authRegiser({
2022-09-09 11:53:35 +00:00
email: emailElement?.value,
password: passElement?.value
})
if (r) {
console.debug('[auth] session update', r)
sessionstore.set(r)
}
}
break
case 'reset':
// send reset-code to login with email
console.log('[auth] reset code: ' + codeElement?.value)
// TODO: authReset(codeElement?.value)
break
case 'resend':
// TODO: authResend(emailElement?.value)
break
case 'forget':
// shows forget mode of auth-modal
if (pass2Element?.value !== passElement?.value) {
setError(t('Passwords are not equal'))
} else {
// TODO: authForget(passElement?.value)
}
break
default:
console.log('[auth] unknown auth mode', mode())
}
}
createEffect(() => {
if (session()?.user?.slug && getModal() === 'auth') {
// hiding itself if finished
console.log('[auth] success, hiding modal')
hideModal()
} else if (session().error) {
console.log('[auth] failure, showing error')
setError(t(statuses[session().error || 'unknown error']))
}
}, [session()])
return (
<div class="row view" classList={{ 'view--sign-up': mode() === 'sign-up' }}>
<div class="col-sm-6 d-md-none auth-image">
<div class="auth-image__text" classList={{ show: mode() === 'sign-up' }}>
<h2>{t('Discours')}</h2>
<h4>{t(`Join the global community of authors!`)}</h4>
<p class="auth-benefits">
{t(
'Get to know the most intelligent people of our time, edit and discuss the articles, share your expertise, rate and decide what to publish in the magazine'
)}
.&nbsp;
{t('New stories every day and even more!')}
</p>
<p class="disclamer">
{t('By signing up you agree with our')}
<a href="/about/terms-of-use" onClick={hideModal}>
{' ' + t('terms of use')}
</a>
, {t('personal data usage and email notifications')}.
</p>
</div>
</div>
<div class="col-sm-6 auth">
<Form
initialValues={initial()}
validation={validation()}
onSubmit={async (form) => {
console.log('[auth] form values', form.values)
}}
>
<div class="auth__inner">
<h4>{titles[mode()]}</h4>
<div class={`auth-subtitle ${mode() === 'forget' ? '' : 'hidden'}`}>
<Show
when={mode() === 'forget'}
fallback={
<Show when={mode() === 'reset'}>
{t('Enter the code or click the link from email to confirm')}
</Show>
}
>
{t('Everything is ok, please give us your email address')}
</Show>
</div>
<Show when={authError()}>
<div class={`auth-info`}>
<ul>
<li class="warn">{authError()}</li>
</ul>
</div>
</Show>
{/*FIXME*/}
{/*<Show when={false && mode() === 'sign-up'}>*/}
{/* <div class='pretty-form__item'>*/}
{/* <input*/}
{/* id='username'*/}
{/* name='username'*/}
{/* autocomplete='username'*/}
{/* ref={usernameElement}*/}
{/* type='text'*/}
{/* placeholder={t('Username')}*/}
{/* />*/}
{/* <label for='username'>{t('Username')}</label>*/}
{/* </div>*/}
{/*</Show>*/}
<Show when={mode() !== 'reset' && mode() !== 'password'}>
<div class="pretty-form__item">
<input
id="email"
name="email"
autocomplete="email"
ref={emailElement}
type="text"
placeholder={t('Email')}
/>
<label for="email">{t('Email')}</label>
</div>
</Show>
<Show when={mode() === 'sign-up' || mode() === 'sign-in' || mode() === 'password'}>
<div class="pretty-form__item">
<input
id="password"
name="password"
autocomplete="current-password"
ref={passElement}
type="password"
placeholder={t('Password')}
/>
<label for="password">{t('Password')}</label>
</div>
</Show>
<Show when={mode() === 'reset'}>
<div class="pretty-form__item">
<input
id="resetcode"
name="resetcode"
ref={codeElement}
value={props.code}
type="text"
placeholder={t('Reset code')}
/>
<label for="resetcode">{t('Reset code')}</label>
</div>
</Show>
<Show when={mode() === 'password' || mode() === 'sign-up'}>
<div class="pretty-form__item">
<input
id="password2"
name="password2"
ref={pass2Element}
type="password"
placeholder={t('Password again')}
autocomplete=""
/>
<label for="password2">{t('Password again')}</label>
</div>
</Show>
<div>
<button class="button submitbtn" disabled={handshaking()} onClick={localAuth}>
{handshaking() ? '...' : titles[mode()]}
</button>
</div>
<Show when={mode() === 'sign-in'}>
<div class="auth-actions">
<a href={''} onClick={() => setMode('forget')}>
{t('Forget password?')}
</a>
</div>
</Show>
<Show when={mode() === 'sign-in' || mode() === 'sign-up'}>
<div class="social-provider">
<div class="providers-text">{t('Or continue with social network')}</div>
<div class="social">
<a href={''} class="facebook-auth" onClick={() => oauth('facebook')}>
<Icon name="facebook" />
</a>
<a href={''} class="google-auth" onClick={() => oauth('google')}>
<Icon name="google" />
</a>
<a href={''} class="vk-auth" onClick={() => oauth('vk')}>
<Icon name="vk" />
</a>
<a href={''} class="github-auth" onClick={() => oauth('github')}>
<Icon name="github" />
</a>
</div>
</div>
</Show>
<div class="auth-control">
<div classList={{ show: mode() === 'sign-up' }}>
<span class="auth-link" onClick={() => setMode('sign-in')}>
{t('I have an account')}
</span>
</div>
<div classList={{ show: mode() === 'sign-in' }}>
<span class="auth-link" onClick={() => setMode('sign-up')}>
{t('I have no account yet')}
</span>
</div>
<div classList={{ show: mode() === 'forget' }}>
<span class="auth-link" onClick={() => setMode('sign-in')}>
{t('I know the password')}
</span>
</div>
<div classList={{ show: mode() === 'reset' }}>
<span class="auth-link" onClick={() => setMode('resend')}>
{t('Resend code')}
</span>
</div>
</div>
</div>
</Form>
</div>
</div>
)
}