[web] Fix FileTreeModalCreateFile modal style on "too many files" error (#28995)

* Temporarily update `maxEntitiesPerProject` to test `FileTreeModalCreateFile`

* Remove unused CSS

* Move project_has_too_many_files Notification to the modal body

* Turn project_approaching_file_limit message into a Notification

* Update project_has_too_many_files translation with `fileCount.limit`

* Update other project_has_too_many_files with limit parameter

* Add translations for project_has_too_many_files_limit

* Revert "Temporarily update `maxEntitiesPerProject` to test `FileTreeModalCreateFile`"

This reverts commit 704996aa96c9ba592c7e44d165def0b97d30bed1.

* Fix unit tests

* Move the warning notification to the modal's body

---------

Co-authored-by: Rebeka <o.dekany@gmail.com>
GitOrigin-RevId: 0f54db7021e4cd4537a14e4f9e1d8ef54337778c
This commit is contained in:
Antoine Clausse
2025-10-14 17:00:47 +02:00
committed by Copybot
parent 03ea7edcfd
commit dd11adfdc3
13 changed files with 55 additions and 50 deletions

View File

@@ -152,7 +152,11 @@ async function addDoc(req, res, next) {
res.json(doc)
} catch (err) {
if (err.message === 'project_has_too_many_files') {
res.status(400).json(req.i18n.translate('project_has_too_many_files'))
res.status(400).json(
req.i18n.translate('project_has_too_many_files_limit', {
limit: Settings.maxEntitiesPerProject,
})
)
} else {
next(err)
}
@@ -178,7 +182,11 @@ async function addFolder(req, res, next) {
res.json(doc)
} catch (err) {
if (err.message === 'project_has_too_many_files') {
res.status(400).json(req.i18n.translate('project_has_too_many_files'))
res.status(400).json(
req.i18n.translate('project_has_too_many_files_limit', {
limit: Settings.maxEntitiesPerProject,
})
)
} else if (err.message === 'invalid element name') {
res.status(400).json(req.i18n.translate('invalid_file_name'))
} else {

View File

@@ -1341,7 +1341,7 @@
"project_files_history": "",
"project_files_outline": "",
"project_flagged_too_many_compiles": "",
"project_has_too_many_files": "",
"project_has_too_many_files_limit": "",
"project_history_labels": "",
"project_last_published_at": "",
"project_linked_to": "",

View File

@@ -16,7 +16,7 @@ export default function ErrorMessage({
error: string | Record<string, any>
}) {
const { t } = useTranslation()
const { isOverleaf } = getMeta('ol-ExposedSettings')
const { isOverleaf, maxEntitiesPerProject } = getMeta('ol-ExposedSettings')
const fileNameLimit = 150
// the error is a string
@@ -27,7 +27,13 @@ export default function ErrorMessage({
return <DangerMessage>{t('file_already_exists')}</DangerMessage>
case 'too-many-files':
return <DangerMessage>{t('project_has_too_many_files')}</DangerMessage>
return (
<DangerMessage>
{t('project_has_too_many_files_limit', {
limit: maxEntitiesPerProject,
})}
</DangerMessage>
)
case 'remote-service-error':
return <DangerMessage>{t('remote_service_error')}</DangerMessage>

View File

@@ -11,6 +11,7 @@ import importOverleafModules from '../../../../../macros/import-overleaf-module.
import { ElementType, lazy, Suspense } from 'react'
import { FullSizeLoadingSpinner } from '@/shared/components/loading-spinner'
import getMeta from '@/utils/meta'
import OLNotification from '@/shared/components/ol/ol-notification'
const createFileModeModules = importOverleafModules('createFileModes') as {
import: { CreateFilePane: ElementType; CreateFileMode: ElementType }
@@ -30,10 +31,20 @@ export default function FileTreeModalCreateFileBody() {
hasLinkUrlFeature,
} = getMeta('ol-ExposedSettings')
if (
!fileCount ||
(typeof fileCount === 'object' && fileCount.status === 'error')
) {
if (typeof fileCount !== 'number' && fileCount.status === 'error') {
return (
<div className="p-4">
<OLNotification
type="error"
content={t('project_has_too_many_files_limit', {
limit: fileCount.limit,
})}
/>
</div>
)
}
if (!fileCount) {
return null
}
@@ -83,6 +94,19 @@ export default function FileTreeModalCreateFileBody() {
<td
className={`modal-new-file-body modal-new-file-body-${newFileCreateMode}`}
>
{typeof fileCount !== 'number' &&
fileCount.status === 'warning' && (
<OLNotification
type="warning"
className={`mb-3 ${newFileCreateMode === 'upload' ? 'mt-0' : 'mt-3'}`}
content={
<>
{t('project_approaching_file_limit')} ({fileCount.value}/
{fileCount.limit})
</>
}
/>
)}
{newFileCreateMode === 'doc' && (
<FileTreeCreateNameProvider initialName="name.tex">
<FileTreeCreateNewDoc />

View File

@@ -1,14 +1,11 @@
import { useTranslation } from 'react-i18next'
import { useFileTreeCreateForm } from '../../contexts/file-tree-create-form'
import { useFileTreeActionable } from '../../contexts/file-tree-actionable'
import { useFileTreeData } from '../../../../shared/context/file-tree-data-context'
import OLButton from '@/shared/components/ol/ol-button'
import OLNotification from '@/shared/components/ol/ol-notification'
export default function FileTreeModalCreateFileFooter() {
const { valid } = useFileTreeCreateForm()
const { newFileCreateMode, inFlight, cancel } = useFileTreeActionable()
const { fileCount } = useFileTreeData()
return (
<FileTreeModalCreateFileFooterContent
@@ -16,26 +13,17 @@ export default function FileTreeModalCreateFileFooter() {
cancel={cancel}
newFileCreateMode={newFileCreateMode}
inFlight={inFlight}
fileCount={fileCount}
/>
)
}
export function FileTreeModalCreateFileFooterContent({
valid,
fileCount,
inFlight,
cancel,
newFileCreateMode,
}: {
valid: boolean
fileCount:
| {
limit: number
status: string
value: number
}
| number
inFlight: boolean
cancel: () => void
newFileCreateMode?: string
@@ -44,23 +32,6 @@ export function FileTreeModalCreateFileFooterContent({
return (
<>
{typeof fileCount !== 'number' && fileCount.status === 'warning' && (
<div className="modal-footer-left approaching-file-limit">
{t('project_approaching_file_limit')} ({fileCount.value}/
{fileCount.limit})
</div>
)}
{typeof fileCount !== 'number' && fileCount.status === 'error' && (
<OLNotification
type="error"
className="at-file-limit"
content={t('project_has_too_many_files')}
>
{/* TODO: add parameter for fileCount.limit */}
</OLNotification>
)}
<OLButton
variant="secondary"
type="button"

View File

@@ -779,16 +779,6 @@
padding: var(--spacing-05);
}
.modal-footer {
.approaching-file-limit {
font-weight: bold;
}
.at-file-limit {
text-align: left;
}
}
/* stylelint-disable selector-class-pattern */
.modal-new-file-body-upload .uppy-Root {
font-family: inherit;

View File

@@ -1387,6 +1387,7 @@
"project_files": "Projektfiler",
"project_flagged_too_many_compiles": "Dette projekt er blevet markeret for at kompilere for ofte. Grænsen bliver snart løftet.",
"project_has_too_many_files": "Dette projekt har nået grænsen på 2000 filer",
"project_has_too_many_files_limit": "Dette projekt har nået grænsen på __limit__ filer",
"project_last_published_at": "Dit projekt var sidst blevet offentliggjort den",
"project_layout_sharing_submission": "Projektlayout, deling og indsendelse",
"project_name": "Projektnavn",

View File

@@ -976,6 +976,7 @@
"project_approaching_file_limit": "Dieses Projekt nähert sich dem Dateilimit",
"project_flagged_too_many_compiles": "Dieses Projekt wurde zu häufig zum Kompilieren vermerkt. Das Limit wird in Kürze aufgehoben.",
"project_has_too_many_files": "Dieses Projekt hat das Limit von 2000 Dateien erreicht",
"project_has_too_many_files_limit": "Dieses Projekt hat das Limit von __limit__ Dateien erreicht",
"project_last_published_at": "Dein Projekt wurde zuletzt veröffentlicht am",
"project_layout_sharing_submission": "Projektlayout, Freigabe und Einreichung",
"project_name": "Projektname",

View File

@@ -1754,6 +1754,7 @@
"project_files_outline": "Project files and outline",
"project_flagged_too_many_compiles": "This project has been flagged for compiling too often. The limit will be lifted shortly.",
"project_has_too_many_files": "This project has reached the 2000 file limit",
"project_has_too_many_files_limit": "This project has reached the __limit__ file limit",
"project_history_labels": "Project history and labels",
"project_last_published_at": "Your project was last published at",
"project_linked_to": "This project is linked to",

View File

@@ -880,6 +880,7 @@
"project_approaching_file_limit": "Ce projet approche la limite de fichiers",
"project_flagged_too_many_compiles": "Ce projet a été compilé trop fréquemment. Cette limite sera levée sous peu.",
"project_has_too_many_files": "La limite des 2 000fichiers a été atteinte pour ce projet",
"project_has_too_many_files_limit": "La limite des __limit__ fichiers a été atteinte pour ce projet",
"project_last_published_at": "Votre projet a été édité pour la dernière fois le",
"project_name": "Nom du projet",
"project_not_linked_to_github": "Ce projet nest pas lié à un dépôt GitHub. Vous pouvez lui créer un dépôt dans GitHub:",

View File

@@ -634,6 +634,7 @@
"project_approaching_file_limit": "Detta projekt närmar sig gränsen för antalet filer",
"project_flagged_too_many_compiles": "Detta projekt har kompilerats för ofta. Begränsningen kommer snart tas bort igen.",
"project_has_too_many_files": "Detta projekt har nått gränsen på 2000 filer",
"project_has_too_many_files_limit": "Detta projekt har nått gränsen på __limit__ filer",
"project_last_published_at": "Ditt projekt blev publicerat senast den",
"project_name": "Projekt namn",
"project_not_linked_to_github": "Detta projekt är inte länkat till ett GitHub repo. Du kan skapa ett repo för det på GitHub:",

View File

@@ -1643,6 +1643,7 @@
"project_files": "项目文件",
"project_flagged_too_many_compiles": "因频繁编译,项目被标旗。编译上限会稍后解除。",
"project_has_too_many_files": "此项目已达到 2000 个文件限制",
"project_has_too_many_files_limit": "此项目已达到 __limit__ 个文件限制",
"project_last_published_at": "您的项目最近一次被发布在",
"project_layout_sharing_submission": "项目布局、分享和提交",
"project_linked_to": "该项目链接到",

View File

@@ -524,7 +524,7 @@ describe('EditorHttpController', function () {
)
await new Promise(resolve => {
ctx.res.callback = () => {
expect(ctx.res.body).to.equal('"project_has_too_many_files"')
expect(ctx.res.body).to.equal('"project_has_too_many_files_limit"')
expect(ctx.res.status).to.have.been.calledWith(400)
resolve()
}
@@ -584,7 +584,7 @@ describe('EditorHttpController', function () {
new Error('project_has_too_many_files')
)
ctx.res.callback = () => {
expect(ctx.res.body).to.equal('"project_has_too_many_files"')
expect(ctx.res.body).to.equal('"project_has_too_many_files_limit"')
expect(ctx.res.statusCode).to.equal(400)
resolve()
}