Skip to content
Snippets Groups Projects
index.tsx 3.71 KiB
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