feat: add in-editor notification when rolling image has updated (#28529)

GitOrigin-RevId: 771773ba1914ef609b6ac84799bdda2d7ae4affa
This commit is contained in:
Jimmy Domagala-Tang
2025-09-24 12:22:14 -04:00
committed by Copybot
parent 0200ad7515
commit 07166bff73
10 changed files with 123 additions and 4 deletions

View File

@@ -167,6 +167,7 @@ function getAllowedImagesForUser(user) {
return {
...image,
allowed: _imageAllowed(user, image),
rolling: image.monthlyExperimental,
}
})

View File

@@ -21,6 +21,7 @@ const VALID_KEYS = [
'ide-redesign-new-survey-promo',
'ide-redesign-beta-intro',
'ide-redesign-labs-user-beta-promo',
'rolling-compile-image-changed',
]
async function completeTutorial(req, res, next) {

View File

@@ -1006,6 +1006,7 @@ module.exports = {
v1ImportDataScreen: [],
snapshotUtils: [],
usGovBanner: [],
rollingBuildsUpdatedAlert: [],
offlineModeToolbarButtons: [],
settingsEntries: [],
autoCompleteExtensions: [],

View File

@@ -21,6 +21,7 @@
"a_new_reference_was_added_from_provider": "",
"a_new_reference_was_added_to_file": "",
"a_new_reference_was_added_to_file_from_provider": "",
"a_new_version_of_the_rolling_texlive_build_released": "",
"about_to_archive_projects": "",
"about_to_delete_cert": "",
"about_to_delete_projects": "",
@@ -1616,6 +1617,7 @@
"showing_x_results_of_total": "",
"sign_up": "",
"simple_search_mode": "",
"since_this_project_is_set_to_the_rolling_build": "",
"single_sign_on_sso": "",
"size": "",
"something_not_right": "",

View File

@@ -2,9 +2,16 @@ import { useTranslation } from 'react-i18next'
import { LostConnectionAlert } from './lost-connection-alert'
import { useConnectionContext } from '@/features/ide-react/context/connection-context'
import { debugging } from '@/utils/debugging'
import { ElementType } from 'react'
import { createPortal } from 'react-dom'
import { useGlobalAlertsContainer } from '@/features/ide-react/context/global-alerts-context'
import OLNotification from '@/shared/components/ol/ol-notification'
import importOverleafModules from '../../../../../macros/import-overleaf-module.macro'
const rollingBuildsUpdatedAlert: Array<{
import: { default: ElementType }
path: string
}> = importOverleafModules('rollingBuildsUpdatedAlert')
export function Alerts() {
const { t } = useTranslation()
@@ -23,6 +30,12 @@ export function Alerts() {
return createPortal(
<>
{rollingBuildsUpdatedAlert.map(
({ import: { default: Component }, path }) => (
<Component key={path} />
)
)}
{connectionState.forceDisconnected &&
// hide "disconnected" banner when displaying out of sync modal
connectionState.error !== 'out-of-sync' ? (

View File

@@ -1,9 +1,13 @@
import { useState } from 'react'
import { useCallback, useState } from 'react'
import LabsExperimentWidget from '../../shared/components/labs/labs-experiments-widget'
import { isInExperiment } from '@/utils/labs-utils'
import { useTranslation } from 'react-i18next'
import MaterialIcon from '@/shared/components/material-icon'
import { isSplitTestEnabled } from '@/utils/splitTestUtils'
import { postJSON } from '@/infrastructure/fetch-json'
import { debugConsole } from '@/utils/debugging'
export const TUTORIAL_KEY = 'rolling-compile-image-changed'
const MonthlyTexliveLabsWidget = ({
labsProgram,
@@ -15,6 +19,18 @@ const MonthlyTexliveLabsWidget = ({
const { t } = useTranslation()
const [optedIn, setOptedIn] = useState(isInExperiment('monthly-texlive'))
const optInWithCompletedTutorial = useCallback(
async (shouldOptIn: boolean) => {
try {
await postJSON(`/tutorial/${TUTORIAL_KEY}/complete`)
} catch (err) {
debugConsole.error(err)
}
setOptedIn(shouldOptIn)
},
[setOptedIn]
)
const monthlyTexLiveSplitTestEnabled = isSplitTestEnabled('monthly-texlive')
if (!monthlyTexLiveSplitTestEnabled) {
return null
@@ -35,7 +51,7 @@ const MonthlyTexliveLabsWidget = ({
labsEnabled={labsProgram}
setErrorMessage={setErrorMessage}
optedIn={optedIn}
setOptedIn={setOptedIn}
setOptedIn={optInWithCompletedTutorial}
title={t('rolling_texlive_build')}
/>
)

View File

@@ -0,0 +1,44 @@
import { useTutorial } from '@/shared/hooks/promotions/use-tutorial'
import { useEditorContext } from '@/shared/context/editor-context'
import { useProjectSettingsContext } from '../editor-left-menu/context/project-settings-context'
import OLNotification from '@/shared/components/ol/ol-notification'
import getMeta from '@/utils/meta'
import { useTranslation } from 'react-i18next'
import { useCallback } from 'react'
export const TUTORIAL_KEY = 'rolling-compile-image-changed'
const rollingImages = getMeta('ol-imageNames')
.filter(img => img.rolling)
.map(img => img.imageName)
const RollingCompileImageChangedAlert = () => {
const { completeTutorial } = useTutorial(TUTORIAL_KEY)
const { inactiveTutorials } = useEditorContext()
const { imageName } = useProjectSettingsContext()
const { t } = useTranslation()
const onClose = useCallback(() => {
completeTutorial({ event: 'promo-click', action: 'complete' })
}, [completeTutorial])
const onRollingBuild = imageName && rollingImages.includes(imageName)
if (inactiveTutorials.includes(TUTORIAL_KEY) || !onRollingBuild) {
return null
}
return (
<OLNotification
className="mt-5"
isDismissible
onDismiss={onClose}
content={t('since_this_project_is_set_to_the_rolling_build')}
type="info"
title={t('a_new_version_of_the_rolling_texlive_build_released')}
/>
)
}
export default RollingCompileImageChangedAlert

View File

@@ -22,6 +22,7 @@
"a_new_reference_was_added_from_provider": "A new reference was added from __provider__",
"a_new_reference_was_added_to_file": "A new reference was added to <0>__filePath__</0>",
"a_new_reference_was_added_to_file_from_provider": "A new reference was added to <0>__filePath__</0> from __provider__",
"a_new_version_of_the_rolling_texlive_build_released": "A new version of the Rolling TeX Live build has been released.",
"about": "About",
"about_to_archive_projects": "You are about to archive the following projects:",
"about_to_delete_cert": "You are about to delete the following certificate:",
@@ -1936,7 +1937,6 @@
"right": "Right",
"ro": "Romanian",
"role": "Role",
"rolling_texlive_build": "Rolling TexLive Build",
"ru": "Russian",
"saml": "SAML",
"saml_auth_error": "Sorry, your identity provider responded with an error. Please contact your administrator for more information.",
@@ -2087,6 +2087,7 @@
"sign_up_for_free": "Sign up for free",
"sign_up_for_free_account": "Sign up for a free account and receive regular updates",
"simple_search_mode": "Simple search",
"since_this_project_is_set_to_the_rolling_build": "Since this project is set to use the rolling build, new compiles will automatically use the newest version.",
"single_sign_on_sso": "Single Sign-On (SSO)",
"site_description": "An online LaTeX editor thats easy to use. No installation, real-time collaboration, version control, hundreds of LaTeX templates, and more.",
"site_wide_option_available": "Site-wide option available",
@@ -2291,7 +2292,6 @@
"test": "Test",
"test_configuration": "Test configuration",
"test_configuration_successful": "Test configuration successful",
"test_more_recent_versions_of_texlive": "Test more recent versions of TexLive before they get turned into our annual release. This experiment adds the compiler option for a monthly build of the current TexLive version. This experimental version is primarily for testing changes to TexLive, packages, and previewing new accessibility features before theyre compiled into our annual TexLive release. Note that these images change month to month, and are untested by our team. Important projects should continue to be built on our yearly release to avoid issues that may occur when the monthly build changes.",
"tex_live_version": "TeX Live version",
"texgpt": "TeXGPT",
"text": "Text",

View File

@@ -0,0 +1,40 @@
import { scriptRunner } from './lib/ScriptRunner.mjs'
import { db } from '../app/src/infrastructure/mongodb.js'
import minimist from 'minimist'
const argv = minimist(process.argv.slice(2))
async function resetTutorials() {
const commit = argv.commit !== undefined
const users = await db.users
.find(
{
'completedTutorials.rolling-compile-image-changed.state': 'completed',
},
{ readPreference: 'secondaryPreferred' }
)
.toArray()
if (!commit) {
console.log(
`would have removed rolling-compile-image-changed tutorial for ${users.length} users`
)
return
}
await db.users.updateMany(
{ _id: { $in: users.map(user => user._id) } },
{
$unset: { 'completedTutorials.rolling-compile-image-changed': '' },
}
)
console.log(`updated ${users.length} users`)
}
try {
await scriptRunner(resetTutorials)
process.exit(0)
} catch (error) {
console.error(error)
process.exit(1)
}

View File

@@ -5,6 +5,7 @@ export type ImageName = {
imageDesc: string
imageName: string
allowed: boolean
rolling?: boolean
}
export type DocId = Brand<string, 'DocId'>