add-email.tsx 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223
  1. import { useState, useEffect } from 'react'
  2. import { useTranslation, Trans } from 'react-i18next'
  3. import { Col } from 'react-bootstrap'
  4. import Cell from './cell'
  5. import Layout from './add-email/layout'
  6. import Input, { DomainInfo } from './add-email/input'
  7. import AddAnotherEmailBtn from './add-email/add-another-email-btn'
  8. import InstitutionFields from './add-email/institution-fields'
  9. import SsoLinkingInfo from './add-email/sso-linking-info'
  10. import AddNewEmailBtn from './add-email/add-new-email-btn'
  11. import useAsync from '../../../../shared/hooks/use-async'
  12. import { useUserEmailsContext } from '../../context/user-email-context'
  13. import { ssoAvailableForDomain } from '../../utils/sso'
  14. import { postJSON } from '../../../../infrastructure/fetch-json'
  15. import { University } from '../../../../../../types/university'
  16. import { CountryCode } from '../../data/countries-list'
  17. import { isValidEmail } from '../../../../shared/utils/email'
  18. import getMeta from '../../../../utils/meta'
  19. import { ReCaptcha2 } from '../../../../shared/components/recaptcha-2'
  20. import { useRecaptcha } from '../../../../shared/hooks/use-recaptcha'
  21. function AddEmail() {
  22. const { t } = useTranslation()
  23. const [isFormVisible, setIsFormVisible] = useState(
  24. () => window.location.hash === '#add-email'
  25. )
  26. const [newEmail, setNewEmail] = useState('')
  27. const [newEmailMatchedDomain, setNewEmailMatchedDomain] =
  28. useState<DomainInfo | null>(null)
  29. const [countryCode, setCountryCode] = useState<CountryCode | null>(null)
  30. const [universities, setUniversities] = useState<
  31. Partial<Record<CountryCode, University[]>>
  32. >({})
  33. const [universityName, setUniversityName] = useState('')
  34. const [role, setRole] = useState('')
  35. const [department, setDepartment] = useState('')
  36. const { isLoading, isError, error, runAsync } = useAsync()
  37. const {
  38. state,
  39. setLoading: setUserEmailsContextLoading,
  40. getEmails,
  41. } = useUserEmailsContext()
  42. const emailAddressLimit = getMeta('ol-emailAddressLimit', 10)
  43. const { ref: recaptchaRef, getReCaptchaToken } = useRecaptcha()
  44. useEffect(() => {
  45. setUserEmailsContextLoading(isLoading)
  46. }, [setUserEmailsContextLoading, isLoading])
  47. const handleShowAddEmailForm = () => {
  48. setIsFormVisible(true)
  49. }
  50. const handleEmailChange = (value: string, domain?: DomainInfo) => {
  51. setNewEmail(value)
  52. setNewEmailMatchedDomain(domain || null)
  53. }
  54. const getSelectedKnownUniversityId = (): number | undefined => {
  55. if (countryCode) {
  56. return universities[countryCode]?.find(
  57. ({ name }) => name === universityName
  58. )?.id
  59. }
  60. return newEmailMatchedDomain?.university.id
  61. }
  62. const handleAddNewEmail = () => {
  63. const selectedKnownUniversityId = getSelectedKnownUniversityId()
  64. const knownUniversityData = selectedKnownUniversityId && {
  65. university: {
  66. id: selectedKnownUniversityId,
  67. },
  68. role,
  69. department,
  70. }
  71. const unknownUniversityData = universityName &&
  72. !selectedKnownUniversityId && {
  73. university: {
  74. name: universityName,
  75. country_code: countryCode,
  76. },
  77. role,
  78. department,
  79. }
  80. runAsync(
  81. (async () => {
  82. const token = await getReCaptchaToken()
  83. await postJSON('/user/emails', {
  84. body: {
  85. email: newEmail,
  86. ...knownUniversityData,
  87. ...unknownUniversityData,
  88. 'g-recaptcha-response': token,
  89. },
  90. })
  91. })()
  92. )
  93. .then(() => {
  94. getEmails()
  95. })
  96. .catch(() => {})
  97. }
  98. if (!isFormVisible) {
  99. return (
  100. <Layout isError={isError} error={error}>
  101. <Col md={12}>
  102. <Cell>
  103. {state.data.emailCount >= emailAddressLimit ? (
  104. <span className="small">
  105. <Trans
  106. i18nKey="email_limit_reached"
  107. values={{
  108. emailAddressLimit,
  109. }}
  110. components={[<strong />]} // eslint-disable-line react/jsx-key
  111. />
  112. </span>
  113. ) : (
  114. <AddAnotherEmailBtn onClick={handleShowAddEmailForm} />
  115. )}
  116. </Cell>
  117. </Col>
  118. </Layout>
  119. )
  120. }
  121. const InputComponent = (
  122. <>
  123. <label htmlFor="affiliations-email" className="sr-only">
  124. {t('email')}
  125. </label>
  126. <Input
  127. onChange={handleEmailChange}
  128. handleAddNewEmail={handleAddNewEmail}
  129. />
  130. </>
  131. )
  132. if (!isValidEmail(newEmail)) {
  133. return (
  134. <Layout isError={isError} error={error}>
  135. <ReCaptcha2 page="addEmail" ref={recaptchaRef} />
  136. <form>
  137. <Col md={8}>
  138. <Cell>
  139. {InputComponent}
  140. <div className="affiliations-table-cell-tabbed">
  141. <div>{t('start_by_adding_your_email')}</div>
  142. </div>
  143. </Cell>
  144. </Col>
  145. <Col md={4}>
  146. <Cell className="text-md-right">
  147. <AddNewEmailBtn email={newEmail} disabled />
  148. </Cell>
  149. </Col>
  150. </form>
  151. </Layout>
  152. )
  153. }
  154. const isSsoAvailableForDomain =
  155. newEmailMatchedDomain && ssoAvailableForDomain(newEmailMatchedDomain)
  156. return (
  157. <Layout isError={isError} error={error}>
  158. <ReCaptcha2 page="addEmail" ref={recaptchaRef} />
  159. <form>
  160. <Col md={8}>
  161. <Cell>
  162. {InputComponent}
  163. {!isSsoAvailableForDomain ? (
  164. <div className="affiliations-table-cell-tabbed">
  165. <InstitutionFields
  166. countryCode={countryCode}
  167. setCountryCode={setCountryCode}
  168. universities={universities}
  169. setUniversities={setUniversities}
  170. universityName={universityName}
  171. setUniversityName={setUniversityName}
  172. role={role}
  173. setRole={setRole}
  174. department={department}
  175. setDepartment={setDepartment}
  176. newEmailMatchedDomain={newEmailMatchedDomain}
  177. />
  178. </div>
  179. ) : null}
  180. </Cell>
  181. </Col>
  182. {!isSsoAvailableForDomain ? (
  183. <Col md={4}>
  184. <Cell className="text-md-right">
  185. <AddNewEmailBtn
  186. email={newEmail}
  187. disabled={isLoading || state.isLoading}
  188. onClick={handleAddNewEmail}
  189. />
  190. </Cell>
  191. </Col>
  192. ) : (
  193. <Col md={12}>
  194. <Cell>
  195. <div className="affiliations-table-cell-tabbed">
  196. <SsoLinkingInfo
  197. email={newEmail}
  198. domainInfo={newEmailMatchedDomain as DomainInfo}
  199. />
  200. </div>
  201. </Cell>
  202. </Col>
  203. )}
  204. </form>
  205. </Layout>
  206. )
  207. }
  208. export default AddEmail