linking-section.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { useState } from 'react'
  2. import { Alert } from 'react-bootstrap'
  3. import { useTranslation } from 'react-i18next'
  4. import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
  5. import { useSSOContext, SSOSubscription } from '../context/sso-context'
  6. import { SSOLinkingWidget } from './linking/sso-widget'
  7. import getMeta from '../../../utils/meta'
  8. function LinkingSection() {
  9. const { t } = useTranslation()
  10. const { subscriptions } = useSSOContext()
  11. const ssoErrorMessage = getMeta('ol-ssoErrorMessage') as string
  12. const projectSyncSuccessMessage = getMeta(
  13. 'ol-projectSyncSuccessMessage'
  14. ) as string
  15. const [integrationLinkingWidgets] = useState<any[]>(
  16. () =>
  17. getMeta('integrationLinkingWidgets') ||
  18. importOverleafModules('integrationLinkingWidgets')
  19. )
  20. const [referenceLinkingWidgets] = useState<any[]>(
  21. () =>
  22. getMeta('referenceLinkingWidgets') ||
  23. importOverleafModules('referenceLinkingWidgets')
  24. )
  25. const hasIntegrationLinkingSection = integrationLinkingWidgets.length
  26. const hasReferencesLinkingSection = referenceLinkingWidgets.length
  27. const hasSSOLinkingSection = Object.keys(subscriptions).length > 0
  28. if (
  29. !hasIntegrationLinkingSection &&
  30. !hasReferencesLinkingSection &&
  31. !hasSSOLinkingSection
  32. ) {
  33. return null
  34. }
  35. return (
  36. <>
  37. <h3>{t('integrations')}</h3>
  38. <p className="small">{t('linked_accounts_explained')}</p>
  39. {hasIntegrationLinkingSection ? (
  40. <>
  41. <h3 id="project-sync" className="text-capitalize">
  42. {t('project_synchronisation')}
  43. </h3>
  44. {projectSyncSuccessMessage ? (
  45. <Alert bsStyle="success">{projectSyncSuccessMessage}</Alert>
  46. ) : null}
  47. <div className="settings-widgets-container">
  48. {integrationLinkingWidgets.map(
  49. ({ import: importObject, path }, widgetIndex) => (
  50. <ModuleLinkingWidget
  51. key={Object.keys(importObject)[0]}
  52. ModuleComponent={Object.values(importObject)[0]}
  53. isLast={widgetIndex === integrationLinkingWidgets.length - 1}
  54. />
  55. )
  56. )}
  57. </div>
  58. </>
  59. ) : null}
  60. {hasReferencesLinkingSection ? (
  61. <>
  62. <h3 id="references" className="text-capitalize">
  63. {t('reference_managers')}
  64. </h3>
  65. <div className="settings-widgets-container">
  66. {referenceLinkingWidgets.map(
  67. ({ import: importObject, path }, widgetIndex) => (
  68. <ModuleLinkingWidget
  69. key={Object.keys(importObject)[0]}
  70. ModuleComponent={Object.values(importObject)[0]}
  71. isLast={widgetIndex === referenceLinkingWidgets.length - 1}
  72. />
  73. )
  74. )}
  75. </div>
  76. </>
  77. ) : null}
  78. {hasSSOLinkingSection ? (
  79. <>
  80. <h3 id="linked-accounts" className="text-capitalize">
  81. {t('linked_accounts')}
  82. </h3>
  83. {ssoErrorMessage ? (
  84. <Alert bsStyle="danger">
  85. {t('sso_link_error')}: {ssoErrorMessage}
  86. </Alert>
  87. ) : null}
  88. <div className="settings-widgets-container">
  89. {Object.values(subscriptions).map(
  90. (subscription, subscriptionIndex) => (
  91. <SSOLinkingWidgetContainer
  92. key={subscription.providerId}
  93. subscription={subscription}
  94. isLast={
  95. subscriptionIndex === Object.keys(subscriptions).length - 1
  96. }
  97. />
  98. )
  99. )}
  100. </div>
  101. </>
  102. ) : null}
  103. {hasIntegrationLinkingSection ||
  104. hasReferencesLinkingSection ||
  105. hasSSOLinkingSection ? (
  106. <hr />
  107. ) : null}
  108. </>
  109. )
  110. }
  111. type LinkingWidgetProps = {
  112. ModuleComponent: any
  113. isLast: boolean
  114. }
  115. function ModuleLinkingWidget({ ModuleComponent, isLast }: LinkingWidgetProps) {
  116. return (
  117. <>
  118. <ModuleComponent />
  119. {isLast ? null : <hr />}
  120. </>
  121. )
  122. }
  123. type SSOLinkingWidgetContainerProps = {
  124. subscription: SSOSubscription
  125. isLast: boolean
  126. }
  127. function SSOLinkingWidgetContainer({
  128. subscription,
  129. isLast,
  130. }: SSOLinkingWidgetContainerProps) {
  131. const { t } = useTranslation()
  132. const { unlink } = useSSOContext()
  133. let description = ''
  134. switch (subscription.providerId) {
  135. case 'collabratec':
  136. description = t('linked_collabratec_description')
  137. break
  138. case 'google':
  139. case 'twitter':
  140. description = `${t('login_with_service', {
  141. service: subscription.provider.name,
  142. })}.`
  143. break
  144. case 'orcid':
  145. description = t('oauth_orcid_description')
  146. break
  147. }
  148. return (
  149. <>
  150. <SSOLinkingWidget
  151. providerId={subscription.providerId}
  152. title={subscription.provider.name}
  153. description={description}
  154. helpPath={subscription.provider.descriptionOptions?.link}
  155. linked={subscription.linked}
  156. linkPath={subscription.provider.linkPath}
  157. onUnlink={() => unlink(subscription.providerId)}
  158. />
  159. {isLast ? null : <hr />}
  160. </>
  161. )
  162. }
  163. export default LinkingSection