diff --git a/services/web/config/settings.defaults.js b/services/web/config/settings.defaults.js
index a7fd72673a..decdbd4af6 100644
--- a/services/web/config/settings.defaults.js
+++ b/services/web/config/settings.defaults.js
@@ -774,6 +774,7 @@ module.exports = {
sourceEditorExtensions: [],
sourceEditorComponents: [],
integrationLinkingWidgets: [],
+ referenceLinkingWidgets: [],
},
moduleImportSequence: ['launchpad', 'server-ce-scripts', 'user-activate'],
diff --git a/services/web/frontend/js/features/settings/components/emails-section.tsx b/services/web/frontend/js/features/settings/components/emails-section.tsx
index b01c87fbb9..e42b69f56a 100644
--- a/services/web/frontend/js/features/settings/components/emails-section.tsx
+++ b/services/web/frontend/js/features/settings/components/emails-section.tsx
@@ -69,9 +69,12 @@ function EmailsSection() {
}
return (
-
-
-
+ <>
+
+
+
+
+ >
)
}
diff --git a/services/web/frontend/js/features/settings/components/integration-linking-section.tsx b/services/web/frontend/js/features/settings/components/integration-linking-section.tsx
deleted file mode 100644
index 4ce5f1456a..0000000000
--- a/services/web/frontend/js/features/settings/components/integration-linking-section.tsx
+++ /dev/null
@@ -1,46 +0,0 @@
-import { useTranslation } from 'react-i18next'
-import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
-const integrationLinkingWidgets = importOverleafModules(
- 'integrationLinkingWidgets'
-)
-
-function IntegrationLinkingSection() {
- const { t } = useTranslation()
-
- return (
- <>
- {t('integrations')}
- {t('linked_accounts_explained')}
-
- {integrationLinkingWidgets.map(
- ({ import: importObject, path }, widgetIndex) => (
-
- )
- )}
-
- >
- )
-}
-
-type IntegrationLinkingWidgetProps = {
- ModuleComponent: any
- isLast: boolean
-}
-
-function IntegrationLinkingWidget({
- ModuleComponent,
- isLast,
-}: IntegrationLinkingWidgetProps) {
- return (
- <>
-
- {isLast ? null :
}
- >
- )
-}
-
-export default IntegrationLinkingSection
diff --git a/services/web/frontend/js/features/settings/components/linking-section.tsx b/services/web/frontend/js/features/settings/components/linking-section.tsx
new file mode 100644
index 0000000000..607b2c6538
--- /dev/null
+++ b/services/web/frontend/js/features/settings/components/linking-section.tsx
@@ -0,0 +1,135 @@
+import { useTranslation } from 'react-i18next'
+import importOverleafModules from '../../../../macros/import-overleaf-module.macro'
+import { useSSOContext, SSOSubscription } from '../context/sso-context'
+import { SSOLinkingWidget } from './linking/sso-widget'
+
+const integrationLinkingWidgets = importOverleafModules(
+ 'integrationLinkingWidgets'
+)
+const referenceLinkingWidgets = importOverleafModules('referenceLinkingWidgets')
+
+function LinkingSection() {
+ const { t } = useTranslation()
+ const { subscriptions } = useSSOContext()
+
+ const hasIntegrationLinkingSection = integrationLinkingWidgets.length
+ const hasReferencesLinkingSection = referenceLinkingWidgets.length
+ const hasSSOLinkingSection = Object.keys(subscriptions).length > 0
+
+ if (
+ !hasIntegrationLinkingSection &&
+ !hasReferencesLinkingSection &&
+ !hasSSOLinkingSection
+ ) {
+ return null
+ }
+
+ return (
+ <>
+ {t('integrations')}
+ {t('linked_accounts_explained')}
+ {hasIntegrationLinkingSection ? (
+ <>
+ {t('sync_dropbox_github')}
+
+ {integrationLinkingWidgets.map(
+ ({ import: importObject, path }, widgetIndex) => (
+
+ )
+ )}
+
+ >
+ ) : null}
+ {hasReferencesLinkingSection ? (
+ <>
+ {t('reference_sync')}
+
+ {referenceLinkingWidgets.map(
+ ({ import: importObject, path }, widgetIndex) => (
+
+ )
+ )}
+
+ >
+ ) : null}
+ {hasSSOLinkingSection ? (
+ <>
+ {t('linked_accounts')}
+
+ {Object.values(subscriptions).map(
+ (subscription, subscriptionIndex) => (
+
+ )
+ )}
+
+ >
+ ) : null}
+ {hasIntegrationLinkingSection ||
+ hasReferencesLinkingSection ||
+ hasSSOLinkingSection ? (
+
+ ) : null}
+ >
+ )
+}
+
+type LinkingWidgetProps = {
+ ModuleComponent: any
+ isLast: boolean
+}
+
+function ModuleLinkingWidget({ ModuleComponent, isLast }: LinkingWidgetProps) {
+ return (
+ <>
+
+ {isLast ? null :
}
+ >
+ )
+}
+
+type SSOLinkingWidgetContainerProps = {
+ subscription: SSOSubscription
+ isLast: boolean
+}
+
+function SSOLinkingWidgetContainer({
+ subscription,
+ isLast,
+}: SSOLinkingWidgetContainerProps) {
+ const { t } = useTranslation()
+ const { unlink } = useSSOContext()
+
+ return (
+ <>
+ unlink(subscription.providerId)}
+ />
+ {isLast ? null :
}
+ >
+ )
+}
+
+export default LinkingSection
diff --git a/services/web/frontend/js/features/settings/components/integration-linking/widget.tsx b/services/web/frontend/js/features/settings/components/linking/integration-widget.tsx
similarity index 100%
rename from services/web/frontend/js/features/settings/components/integration-linking/widget.tsx
rename to services/web/frontend/js/features/settings/components/linking/integration-widget.tsx
diff --git a/services/web/frontend/js/features/settings/components/sso-linking/widget.tsx b/services/web/frontend/js/features/settings/components/linking/sso-widget.tsx
similarity index 100%
rename from services/web/frontend/js/features/settings/components/sso-linking/widget.tsx
rename to services/web/frontend/js/features/settings/components/linking/sso-widget.tsx
diff --git a/services/web/frontend/js/features/settings/components/root.tsx b/services/web/frontend/js/features/settings/components/root.tsx
index f181838b03..27b6f2f464 100644
--- a/services/web/frontend/js/features/settings/components/root.tsx
+++ b/services/web/frontend/js/features/settings/components/root.tsx
@@ -5,12 +5,12 @@ import getMeta from '../../../utils/meta'
import EmailsSection from './emails-section'
import AccountInfoSection from './account-info-section'
import PasswordSection from './password-section'
-import IntegrationLinkingSection from './integration-linking-section'
-import SSOLinkingSection from './sso-linking-section'
+import LinkingSection from './linking-section'
import MiscSection from './misc-section'
import LeaveSection from './leave-section'
import * as eventTracking from '../../../infrastructure/event-tracking'
import { UserProvider } from '../../../shared/context/user-context'
+import { SSOProvider } from '../context/sso-context'
function SettingsPageRoot() {
const { t } = useTranslation()
@@ -34,7 +34,7 @@ function SettingsPageRoot() {
{t('account_settings')}
-
+
@@ -45,10 +45,9 @@ function SettingsPageRoot() {
-
-
-
-
+
+
+
diff --git a/services/web/frontend/js/features/settings/components/sso-linking-section.tsx b/services/web/frontend/js/features/settings/components/sso-linking-section.tsx
deleted file mode 100644
index b8a216f20f..0000000000
--- a/services/web/frontend/js/features/settings/components/sso-linking-section.tsx
+++ /dev/null
@@ -1,68 +0,0 @@
-import { useTranslation } from 'react-i18next'
-import {
- SSOProvider,
- useSSOContext,
- SSOSubscription,
-} from '../context/sso-context'
-import { SSOLinkingWidget } from './sso-linking/widget'
-
-function SSOLinkingSection() {
- const { t } = useTranslation()
-
- return (
-
- {t('linked_accounts')}
- {t('linked_accounts_explained')}
-
-
- )
-}
-
-function SSOLinkingWidgets() {
- const { subscriptions } = useSSOContext()
-
- return (
-
- {Object.values(subscriptions).map((subscription, subscriptionIndex) => (
-
- ))}
-
- )
-}
-
-type SSOLinkingWidgetContainerProps = {
- subscription: SSOSubscription
- isLast: boolean
-}
-
-function SSOLinkingWidgetContainer({
- subscription,
- isLast,
-}: SSOLinkingWidgetContainerProps) {
- const { t } = useTranslation()
- const { unlink } = useSSOContext()
-
- return (
- <>
-
unlink(subscription.providerId)}
- />
- {isLast ? null :
}
- >
- )
-}
-
-export default SSOLinkingSection
diff --git a/services/web/frontend/stories/settings/helpers/integration-linking.js b/services/web/frontend/stories/settings/helpers/integration-linking.js
deleted file mode 100644
index cf7df7cc7f..0000000000
--- a/services/web/frontend/stories/settings/helpers/integration-linking.js
+++ /dev/null
@@ -1,21 +0,0 @@
-const MOCK_DELAY = 1000
-
-export function defaultSetupMocks(fetchMock) {
- fetchMock.get(
- 'express:/user/tpds/queues',
- { tpdsToWeb: 0, webToTpds: 0 },
- { delay: MOCK_DELAY }
- )
-}
-
-export function setDefaultMeta() {
- window.metaAttributesCache.set('ol-user', {
- features: { github: true, dropbox: true, mendeley: false, zotero: false },
- refProviders: {
- mendeley: true,
- zotero: true,
- },
- })
- window.metaAttributesCache.set('ol-github', { enabled: false })
- window.metaAttributesCache.set('ol-dropbox', { registered: true })
-}
diff --git a/services/web/frontend/stories/settings/helpers/sso-linking.js b/services/web/frontend/stories/settings/helpers/linking.js
similarity index 66%
rename from services/web/frontend/stories/settings/helpers/sso-linking.js
rename to services/web/frontend/stories/settings/helpers/linking.js
index 3cdc6cf6dc..647fcb0427 100644
--- a/services/web/frontend/stories/settings/helpers/sso-linking.js
+++ b/services/web/frontend/stories/settings/helpers/linking.js
@@ -1,10 +1,25 @@
const MOCK_DELAY = 1000
export function defaultSetupMocks(fetchMock) {
- fetchMock.post('/user/oauth-unlink', 200, { delay: MOCK_DELAY })
+ fetchMock
+ .post('/user/oauth-unlink', 200, { delay: MOCK_DELAY })
+ .get(
+ 'express:/user/tpds/queues',
+ { tpdsToWeb: 0, webToTpds: 0 },
+ { delay: MOCK_DELAY }
+ )
}
export function setDefaultMeta() {
+ window.metaAttributesCache.set('ol-user', {
+ features: { github: true, dropbox: true, mendeley: false, zotero: false },
+ refProviders: {
+ mendeley: true,
+ zotero: true,
+ },
+ })
+ window.metaAttributesCache.set('ol-github', { enabled: false })
+ window.metaAttributesCache.set('ol-dropbox', { registered: true })
window.metaAttributesCache.set('ol-thirdPartyIds', {
collabratec: 'collabratec-id',
google: 'google-id',
diff --git a/services/web/frontend/stories/settings/integration-linking.stories.js b/services/web/frontend/stories/settings/integration-linking.stories.js
deleted file mode 100644
index 774e9d03f2..0000000000
--- a/services/web/frontend/stories/settings/integration-linking.stories.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import useFetchMock from '../hooks/use-fetch-mock'
-import IntegrationLinkingSection from '../../js/features/settings/components/integration-linking-section'
-import {
- setDefaultMeta,
- defaultSetupMocks,
-} from './helpers/integration-linking'
-import { UserProvider } from '../../js/shared/context/user-context'
-
-export const Section = args => {
- useFetchMock(defaultSetupMocks)
- setDefaultMeta()
-
- return (
-
-
-
- )
-}
-
-export default {
- title: 'Account Settings / Integration Linking / Section',
- component: IntegrationLinkingSection,
-}
diff --git a/services/web/frontend/stories/settings/linking.stories.js b/services/web/frontend/stories/settings/linking.stories.js
new file mode 100644
index 0000000000..6c45a33e91
--- /dev/null
+++ b/services/web/frontend/stories/settings/linking.stories.js
@@ -0,0 +1,46 @@
+import useFetchMock from '../hooks/use-fetch-mock'
+import LinkingSection from '../../js/features/settings/components/linking-section'
+import { setDefaultMeta, defaultSetupMocks } from './helpers/linking'
+import { UserProvider } from '../../js/shared/context/user-context'
+import { SSOProvider } from '../../js/features/settings/context/sso-context'
+
+export const Section = args => {
+ useFetchMock(defaultSetupMocks)
+ setDefaultMeta()
+
+ return (
+
+
+
+
+
+ )
+}
+
+export const SectionAllUnlinked = args => {
+ useFetchMock(defaultSetupMocks)
+ setDefaultMeta()
+ window.metaAttributesCache.set('ol-thirdPartyIds', {})
+ window.metaAttributesCache.set('ol-user', {
+ features: { github: true, dropbox: true, mendeley: true, zotero: true },
+ refProviders: {
+ mendeley: false,
+ zotero: false,
+ },
+ })
+ window.metaAttributesCache.set('ol-github', { enabled: false })
+ window.metaAttributesCache.set('ol-dropbox', { registered: false })
+
+ return (
+
+
+
+
+
+ )
+}
+
+export default {
+ title: 'Account Settings / Linking',
+ component: LinkingSection,
+}
diff --git a/services/web/frontend/stories/settings/page.stories.js b/services/web/frontend/stories/settings/page.stories.js
index 9a28ac1dd9..5e07a7e71f 100644
--- a/services/web/frontend/stories/settings/page.stories.js
+++ b/services/web/frontend/stories/settings/page.stories.js
@@ -17,13 +17,9 @@ import {
defaultSetupMocks as defaultSetupEmailsMocks,
} from './helpers/emails'
import {
- setDefaultMeta as setDefaultIntegrationLinkingMeta,
- defaultSetupMocks as defaultSetupIntegrationLinkingMocks,
-} from './helpers/integration-linking'
-import {
- setDefaultMeta as setDefaultSSOMeta,
- defaultSetupMocks as defaultSetupSSOMocks,
-} from './helpers/sso-linking'
+ setDefaultMeta as setDefaultLinkingMeta,
+ defaultSetupMocks as defaultSetupLinkingMocks,
+} from './helpers/linking'
import { UserProvider } from '../../js/shared/context/user-context'
export const Root = args => {
@@ -31,14 +27,12 @@ export const Root = args => {
setDefaultAccountInfoMeta()
setDefaultPasswordMeta()
setDefaultEmailsMeta()
- setDefaultIntegrationLinkingMeta()
- setDefaultSSOMeta()
+ setDefaultLinkingMeta()
useFetchMock(defaultSetupLeaveMocks)
useFetchMock(defaultSetupAccountInfoMocks)
useFetchMock(defaultSetupPasswordMocks)
useFetchMock(defaultSetupEmailsMocks)
- useFetchMock(defaultSetupIntegrationLinkingMocks)
- useFetchMock(defaultSetupSSOMocks)
+ useFetchMock(defaultSetupLinkingMocks)
return (
diff --git a/services/web/frontend/stories/settings/sso-linking.stories.js b/services/web/frontend/stories/settings/sso-linking.stories.js
deleted file mode 100644
index 84ffa29553..0000000000
--- a/services/web/frontend/stories/settings/sso-linking.stories.js
+++ /dev/null
@@ -1,23 +0,0 @@
-import useFetchMock from '../hooks/use-fetch-mock'
-import SSOLinkingSection from '../../js/features/settings/components/sso-linking-section'
-import { setDefaultMeta, defaultSetupMocks } from './helpers/sso-linking'
-
-export const Section = args => {
- useFetchMock(defaultSetupMocks)
- setDefaultMeta()
-
- return
-}
-
-export const SectionAllUnlinked = args => {
- useFetchMock(defaultSetupMocks)
- setDefaultMeta()
- window.metaAttributesCache.set('ol-thirdPartyIds', {})
-
- return
-}
-
-export default {
- title: 'Account Settings / SSO Linking / Section',
- component: SSOLinkingSection,
-}
diff --git a/services/web/test/frontend/features/settings/components/integration-linking/widget.test.tsx b/services/web/test/frontend/features/settings/components/linking/integration-widget.test.tsx
similarity index 98%
rename from services/web/test/frontend/features/settings/components/integration-linking/widget.test.tsx
rename to services/web/test/frontend/features/settings/components/linking/integration-widget.test.tsx
index 1c9bdc06e2..faf9cdaae6 100644
--- a/services/web/test/frontend/features/settings/components/integration-linking/widget.test.tsx
+++ b/services/web/test/frontend/features/settings/components/linking/integration-widget.test.tsx
@@ -1,6 +1,6 @@
import { expect } from 'chai'
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
-import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/integration-linking/widget'
+import { IntegrationLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/integration-widget'
describe('', function () {
const defaultProps = {
diff --git a/services/web/test/frontend/features/settings/components/sso-linking/widget.test.tsx b/services/web/test/frontend/features/settings/components/linking/sso-widget.test.tsx
similarity index 98%
rename from services/web/test/frontend/features/settings/components/sso-linking/widget.test.tsx
rename to services/web/test/frontend/features/settings/components/linking/sso-widget.test.tsx
index e8dd9d6478..13a95fe5ce 100644
--- a/services/web/test/frontend/features/settings/components/sso-linking/widget.test.tsx
+++ b/services/web/test/frontend/features/settings/components/linking/sso-widget.test.tsx
@@ -1,7 +1,7 @@
import { expect } from 'chai'
import sinon from 'sinon'
import { screen, fireEvent, render, waitFor } from '@testing-library/react'
-import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/sso-linking/widget'
+import { SSOLinkingWidget } from '../../../../../../frontend/js/features/settings/components/linking/sso-widget'
describe('', function () {
const defaultProps = {