import { useEffect, useState } from 'react' import { useTranslation } from 'react-i18next' import Page from 'components/page' import Loading from 'components/loading' import { styled } from '@mui/material/styles' import { useUserContext } from 'contexts' import { HrefButton } from 'components/button' import { HrefLineButton } from 'components/button/linebutton' import { submitJsonOpts } from 'utils' const FlexDiv = styled('div')(() => ({ display: 'flex', gap: '0.5rem', })) function ChooseRegistrationMethod() { const { t } = useTranslation(['invite']) return ( <Page> <h1>{t('header')}</h1> <p>{t('description')}</p> <FlexDiv> <HrefButton to="/oidc/authenticate/">{t('login')}</HrefButton> <HrefLineButton to="/guestregister">{t('manual')}</HrefLineButton> </FlexDiv> </Page> ) } interface ShowFeedbackProps { title: string description: string } function ShowFeedback(props: ShowFeedbackProps) { const { title, description } = props return ( <Page> <h1>{title}</h1> <p>{description}</p> </Page> ) } function Invite() { const { t } = useTranslation(['invite']) const { user, fetchUserInfo } = useUserContext() const [inviteToken, setInviteToken] = useState('') const [tokenChecked, setTokenChecked] = useState(false) const [isCheckingToken, setIsCheckingToken] = useState(false) const [tokenOk, setTokenOk] = useState(false) const [checkError, setCheckError] = useState('') async function checkToken(token: string) { try { const response = await fetch( '/api/ui/v1/invitecheck/', submitJsonOpts('POST', { invite_token: token }) ) if (response.status === 200) { setTokenOk(true) return } const data = await response.json() if ('code' in data) { setCheckError(data.code) } else { setCheckError('unknown') } } catch (error) { console.error(error) setCheckError('unknown') } finally { setTokenChecked(true) setIsCheckingToken(false) } } console.log({ inviteToken, isCheckingToken, tokenChecked, tokenOk, user }) useEffect(() => { setIsCheckingToken(true) // This may seem unecessary, but race conditions have been // observed where the userinfo endpoint is called too fast // and no invite_id is found in the server-side session setTimeout(fetchUserInfo, 100) }, [setTokenOk]) if (user.auth) { return <ChooseRegistrationMethod /> } if (isCheckingToken || (tokenOk && !user.auth)) { return <Loading /> } if (inviteToken !== '' && !tokenChecked) { checkToken(inviteToken) return <Loading /> } if (checkError !== '') { if ( checkError === 'missing_invite_token' || checkError === 'invalid_invite_token' ) { // Missing or invalid token return ( <ShowFeedback title={t('common:error.error')} description={t('invite:errors.invalidToken')} /> ) } if (checkError === 'expired_invite_token') { // Expired token return ( <ShowFeedback title={t('common:error.error')} description={t('invite:errors.expiredToken')} /> ) } // Unknown error return ( <ShowFeedback title={t('common:error.error')} description={t('common:error.unknown')} /> ) } const providedToken = window.location.hash.slice(1).trim() if (!inviteToken && providedToken) { setInviteToken(providedToken) return <Loading /> } // We'll end up here if no token was provided in the URL return ( <ShowFeedback title={t('common:error.error')} description={t('invite:errors.invalidToken')} /> ) } export default Invite