diff --git a/services/web/app/src/Features/User/UserPagesController.mjs b/services/web/app/src/Features/User/UserPagesController.mjs index edf00d1960..1a7c57dd45 100644 --- a/services/web/app/src/Features/User/UserPagesController.mjs +++ b/services/web/app/src/Features/User/UserPagesController.mjs @@ -12,22 +12,28 @@ import Features from '../../infrastructure/Features.mjs' import Modules from '../../infrastructure/Modules.mjs' import SplitTestHandler from '../SplitTests/SplitTestHandler.mjs' +function popSessionValue(session, key) { + const value = session[key] + if (value) { + delete session[key] + } + return value +} + async function settingsPage(req, res) { const userId = SessionManager.getLoggedInUserId(req.session) const reconfirmationRemoveEmail = req.query.remove // SSO - const ssoError = req.session.ssoError - if (ssoError) { - delete req.session.ssoError - } - const ssoErrorMessage = req.session.ssoErrorMessage - if (ssoErrorMessage) { - delete req.session.ssoErrorMessage - } - const projectSyncSuccessMessage = req.session.projectSyncSuccessMessage - if (projectSyncSuccessMessage) { - delete req.session.projectSyncSuccessMessage - } + delete req.session.ssoError + const ssoErrorMessage = popSessionValue(req.session, 'ssoErrorMessage') + const projectSyncSuccessMessage = popSessionValue( + req.session, + 'projectSyncSuccessMessage' + ) + const projectSyncErrorMessage = popSessionValue( + req.session, + 'projectSyncErrorMessage' + ) // Institution SSO let institutionLinked = _.get(req.session, ['saml', 'linked']) if (institutionLinked) { @@ -168,6 +174,7 @@ async function settingsPage(req, res) { ssoErrorMessage, thirdPartyIds: UserPagesController._restructureThirdPartyIds(user), projectSyncSuccessMessage, + projectSyncErrorMessage, personalAccessTokens, emailAddressLimit: Settings.emailAddressLimit, isManagedAccount: !!req.managedBy, diff --git a/services/web/app/views/user/settings.pug b/services/web/app/views/user/settings.pug index d690f0b4e1..534ba2c992 100644 --- a/services/web/app/views/user/settings.pug +++ b/services/web/app/views/user/settings.pug @@ -42,6 +42,7 @@ block append meta meta(name='ol-dropbox' data-type='json' content=dropbox) meta(name='ol-github' data-type='json' content=github) meta(name='ol-projectSyncSuccessMessage' content=projectSyncSuccessMessage) + meta(name='ol-projectSyncErrorMessage' content=projectSyncErrorMessage) meta( name='ol-personalAccessTokens' data-type='json' diff --git a/services/web/frontend/js/features/settings/components/linking-section.tsx b/services/web/frontend/js/features/settings/components/linking-section.tsx index aa1115c569..c4694d43bc 100644 --- a/services/web/frontend/js/features/settings/components/linking-section.tsx +++ b/services/web/frontend/js/features/settings/components/linking-section.tsx @@ -24,6 +24,7 @@ function LinkingSection() { const ssoErrorMessage = getMeta('ol-ssoErrorMessage') const cannotUseAi = getMeta('ol-cannot-use-ai') const projectSyncSuccessMessage = getMeta('ol-projectSyncSuccessMessage') + const projectSyncErrorMessage = getMeta('ol-projectSyncErrorMessage') // hide linking widgets in CI const integrationLinkingWidgets = getMeta('ol-hideLinkingWidgets') @@ -111,6 +112,9 @@ function LinkingSection() { /> ) : null} + {projectSyncErrorMessage ? ( + + ) : null}
{allIntegrationLinkingWidgets.map( ({ import: importObject }, widgetIndex) => ( diff --git a/services/web/frontend/js/utils/meta.ts b/services/web/frontend/js/utils/meta.ts index e481e5ec2b..bfe14e656f 100644 --- a/services/web/frontend/js/utils/meta.ts +++ b/services/web/frontend/js/utils/meta.ts @@ -138,7 +138,7 @@ export interface Meta { 'ol-galleryTagName': string 'ol-gitBridgeEnabled': boolean 'ol-gitBridgePublicBaseUrl': string - 'ol-github': { enabled: boolean; error: boolean } + 'ol-github': { enabled: boolean; error: boolean; expired?: boolean } 'ol-groupAuditLogs': [] 'ol-groupDomains': [] 'ol-groupId': string @@ -259,6 +259,7 @@ export interface Meta { 'ol-projectEntityCounts': { files: number; docs: number } 'ol-projectHistoryFailures': FullHistoryFailure[] 'ol-projectName': string + 'ol-projectSyncErrorMessage': string 'ol-projectSyncSuccessMessage': string 'ol-projectTags': Tag[] 'ol-project_id': string diff --git a/services/web/locales/en.json b/services/web/locales/en.json index aa7eb4d50b..78aac6ab39 100644 --- a/services/web/locales/en.json +++ b/services/web/locales/en.json @@ -1048,6 +1048,7 @@ "github_sync_description": "With GitHub Sync you can link your __appName__ projects to GitHub repositories, create new commits from __appName__, and merge commits from GitHub.", "github_sync_error": "Sorry, there was a problem checking our GitHub service. Please try again in a few moments.", "github_sync_repository_not_found_description": "The linked repository has either been removed, or you no longer have access to it. You can set up sync with a new repository by cloning the project and using the ‘GitHub’ menu item. You can also unlink the repository from this project.", + "github_sync_unlink_error": "Sorry, we weren’t able to unlink your GitHub account. Please try again in a few moments.", "github_timeout_error": "Syncing your Overleaf project with GitHub has timed out. This may be due to the overall size of your project, or the number of files/changes to sync, being too large.", "github_too_many_files_error": "This repository cannot be imported as it exceeds the maximum number of files allowed", "github_validation_check": "Please check that the repository name is valid, and that you have permission to create the repository.", @@ -2891,7 +2892,7 @@ "unlink_dropbox_warning": "Any projects that you have synced with Dropbox will be disconnected and no longer kept in sync with Dropbox. Are you sure you want to unlink your Dropbox account?", "unlink_from_sso": "Unlink from SSO", "unlink_github_repository": "Unlink GitHub repository", - "unlink_github_warning": "Any projects that you have synced with GitHub will be disconnected and no longer kept in sync with GitHub. Are you sure you want to unlink your GitHub account?", + "unlink_github_warning": "Syncing will pause until you relink your GitHub account. Your projects’ GitHub connections won’t be lost, and syncing will resume automatically once you relink. Are you sure you want to unlink your GitHub account?", "unlink_linked_accounts": "Unlink any linked accounts (such as ORCID ID, IEEE). <0>Remove them in Account settings (under Linked Accounts).", "unlink_linked_google_account": "Unlink your Google account. <0>Remove it in Account settings (under Linked Accounts).", "unlink_provider_account_title": "Unlink __provider__ Account", diff --git a/services/web/test/unit/src/User/UserPagesController.test.mjs b/services/web/test/unit/src/User/UserPagesController.test.mjs index dd403df881..be7660d621 100644 --- a/services/web/test/unit/src/User/UserPagesController.test.mjs +++ b/services/web/test/unit/src/User/UserPagesController.test.mjs @@ -428,6 +428,38 @@ describe('UserPagesController', function () { }) }) + it("should set and clear 'projectSyncErrorMessage'", async function (ctx) { + ctx.req.session.projectSyncErrorMessage = 'Some Sync Error' + await new Promise((resolve, reject) => { + ctx.res.callback = () => { + ctx.res.renderedVariables.projectSyncErrorMessage.should.equal( + 'Some Sync Error' + ) + expect(ctx.req.session.projectSyncErrorMessage).to.not.exist + resolve() + } + ctx.UserPagesController.settingsPage( + ctx.req, + ctx.res, + ctx.rejectOnError(reject) + ) + }) + }) + + it("should leave 'projectSyncErrorMessage' undefined when not set on the session", async function (ctx) { + await new Promise((resolve, reject) => { + ctx.res.callback = () => { + expect(ctx.res.renderedVariables.projectSyncErrorMessage).to.not.exist + resolve() + } + ctx.UserPagesController.settingsPage( + ctx.req, + ctx.res, + ctx.rejectOnError(reject) + ) + }) + }) + it('should cast refProviders to booleans', async function (ctx) { await new Promise((resolve, reject) => { ctx.res.callback = () => {