From 291f882aa282023faf58cb6d2f08a8b982ac843a Mon Sep 17 00:00:00 2001 From: ilkin-overleaf <100852799+ilkin-overleaf@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:45:24 +0200 Subject: [PATCH] Merge pull request #30514 from overleaf/ii-cms-launchpad-typescript [web] Convert cms and launchpad to TypeScript GitOrigin-RevId: d2b38671a2389206dafc7bd50c28cf0bb9683601 --- services/web/frontend/js/utils/meta.ts | 1 + .../launchpad/frontend/js/pages/launchpad.js | 82 -------------- .../launchpad/frontend/js/pages/launchpad.ts | 103 ++++++++++++++++++ 3 files changed, 104 insertions(+), 82 deletions(-) delete mode 100644 services/web/modules/launchpad/frontend/js/pages/launchpad.js create mode 100644 services/web/modules/launchpad/frontend/js/pages/launchpad.ts diff --git a/services/web/frontend/js/utils/meta.ts b/services/web/frontend/js/utils/meta.ts index 5666a120f2..e910b54335 100644 --- a/services/web/frontend/js/utils/meta.ts +++ b/services/web/frontend/js/utils/meta.ts @@ -76,6 +76,7 @@ export interface Meta { > 'ol-adminCapabilities': AdminCapability[] 'ol-adminSubscription': AdminSubscription + 'ol-adminUserExists': boolean 'ol-aiAssistViaWritefullSource': string 'ol-algolia': AlgoliaConfig | undefined 'ol-allInReconfirmNotificationPeriods': UserEmailData[] diff --git a/services/web/modules/launchpad/frontend/js/pages/launchpad.js b/services/web/modules/launchpad/frontend/js/pages/launchpad.js deleted file mode 100644 index 0c6b0b8cac..0000000000 --- a/services/web/modules/launchpad/frontend/js/pages/launchpad.js +++ /dev/null @@ -1,82 +0,0 @@ -/* global io */ -import '@/marketing' -import { - inflightHelper, - toggleDisplay, -} from '../../../../../frontend/js/features/form-helpers/hydrate-form' -import getMeta from '../../../../../frontend/js/utils/meta' - -function setUpStatusIndicator(el, fn) { - inflightHelper(el) - - const displaySuccess = el.querySelectorAll('[data-ol-result="success"]') - const displayError = el.querySelectorAll('[data-ol-result="error"]') - - // The checks are very lightweight and do not appear to do anything - // from looking at the UI. Add an artificial delay of 1s to show that - // we are actually doing something. :) - const artificialProgressDelay = 1000 - - function run() { - setTimeout(() => { - fn() - .then(() => { - toggleDisplay(displayError, displaySuccess) - }) - .catch(error => { - el.querySelector('[data-ol-error]').textContent = error.message - toggleDisplay(displaySuccess, displayError) - }) - .finally(() => { - el.dispatchEvent(new Event('idle')) - }) - }, artificialProgressDelay) - } - - el.querySelectorAll('button').forEach(retryBtn => { - retryBtn.addEventListener('click', function (e) { - e.preventDefault() - el.dispatchEvent(new Event('pending')) - run() - }) - }) - - run() -} - -function setUpStatusIndicators() { - setUpStatusIndicator( - document.querySelector('[data-ol-launchpad-check="websocket"]'), - () => { - const timeout = 10 * 1000 - const socket = io.connect(null, { - reconnect: false, - 'connect timeout': timeout, - 'force new connection': true, - query: new URLSearchParams({ - projectId: '404404404404404404404404', - }).toString(), - }) - return new Promise((resolve, reject) => { - setTimeout(() => reject(new Error('timed out')), timeout) - socket.on('connectionRejected', function (err) { - if (err.code === 'ProjectNotFound') { - // We received the response from joinProject, so the websocket is up. - resolve() - } else { - reject(new Error(err && err.message)) - } - }) - socket.on('connect_failed', function (err) { - reject(new Error(err && err.message)) - }) - }).finally(() => { - socket.disconnect() - }) - } - ) -} - -if (getMeta('ol-adminUserExists')) { - setUpStatusIndicators() -} diff --git a/services/web/modules/launchpad/frontend/js/pages/launchpad.ts b/services/web/modules/launchpad/frontend/js/pages/launchpad.ts new file mode 100644 index 0000000000..ecde628829 --- /dev/null +++ b/services/web/modules/launchpad/frontend/js/pages/launchpad.ts @@ -0,0 +1,103 @@ +import '@/marketing' +import { + inflightHelper, + toggleDisplay, +} from '@/features/form-helpers/hydrate-form' +import getMeta from '@/utils/meta' +import { Socket } from '@/features/ide-react/connection/types/socket' + +declare const io: { + connect: (url: string | null, options?: Record) => Socket +} + +interface ConnectionRejectedError { + code?: string + message?: string +} + +function setUpStatusIndicator(el: HTMLElement, fn: () => Promise) { + inflightHelper(el) + + const displaySuccess = el.querySelectorAll( + '[data-ol-result="success"]' + ) + const displayError = el.querySelectorAll( + '[data-ol-result="error"]' + ) + + // The checks are very lightweight and do not appear to do anything + // from looking at the UI. Add an artificial delay of 1s to show that + // we are actually doing something. :) + const artificialProgressDelay = 1000 + + function run() { + setTimeout(() => { + fn() + .then(() => { + toggleDisplay(displayError, displaySuccess) + }) + .catch(error => { + const errorElement = el.querySelector('[data-ol-error]') + if (errorElement) { + errorElement.textContent = error.message + } + toggleDisplay(displaySuccess, displayError) + }) + .finally(() => { + el.dispatchEvent(new Event('idle')) + }) + }, artificialProgressDelay) + } + + el.querySelectorAll('button').forEach(retryBtn => { + retryBtn.addEventListener('click', function (e) { + e.preventDefault() + el.dispatchEvent(new Event('pending')) + run() + }) + }) + + run() +} + +function setUpStatusIndicators() { + const launchpadCheckElement = document.querySelector( + '[data-ol-launchpad-check="websocket"]' + ) + + if (!launchpadCheckElement) { + return + } + + setUpStatusIndicator(launchpadCheckElement, () => { + const timeout = 10 * 1000 + const socket = io.connect(null, { + reconnect: false, + 'connect timeout': timeout, + 'force new connection': true, + query: new URLSearchParams({ + projectId: '404404404404404404404404', + }).toString(), + }) + return new Promise((resolve, reject) => { + setTimeout(() => reject(new Error('timed out')), timeout) + socket.on('connectionRejected', function (err?: ConnectionRejectedError) { + if (err?.code === 'ProjectNotFound') { + // We received the response from joinProject, so the websocket is up. + resolve() + } else { + reject(new Error(err && err.message)) + } + }) + socket.on('connect_failed', function (err?: Error) { + reject(new Error(err && err.message)) + }) + }).finally(() => { + socket.disconnect() + }) + }) +} + +if (getMeta('ol-adminUserExists')) { + setUpStatusIndicators() +}