From 039ab2a29645771f5e13e2910fac3c6da31f07bb Mon Sep 17 00:00:00 2001 From: Anthony LC Date: Mon, 5 Oct 2026 15:25:30 +0200 Subject: [PATCH] =?UTF-8?q?=F0=9F=A5=85(frontend)=20change=20ui=20when=20c?= =?UTF-8?q?atch=20unexpected=20upload=20error?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit We updated the UI to provide a clearer indication when an unexpected upload error occurs. Before the error was displayed on the top of the editor, making it less noticeable to the user, now we display the error directly within the upload block, making it more visible to the user and more user-friendly. --- .../__tests__/app-impress/doc-editor.spec.ts | 6 +-- .../doc-editor/components/BlockNoteEditor.tsx | 13 +----- .../docs/doc-editor/hook/useUploadFile.tsx | 43 +++++++++++++++++-- 3 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts index 7aabc689c..4af5cd8dc 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts @@ -356,20 +356,20 @@ test.describe('Doc Editor', () => { const fileChooser = await fileChooserPromise; await fileChooser.setFiles(path.join(__dirname, 'assets/test.html')); - await expect(editor.getByText('Analyzing file...')).toBeVisible(); + await expect(editor.getByText('Analyzing the file')).toBeVisible(); // To be sure the retry happens even after a page reload await goToGridDoc(page, { title: randomDoc, }); - await expect(editor.getByText('Analyzing file...')).toBeVisible(); + await expect(editor.getByText('Analyzing the file')).toBeVisible(); // The retry takes a few seconds await expect(editor.getByText('test.html')).toBeVisible({ timeout: 7000, }); - await expect(editor.getByText('Analyzing file...')).toBeHidden(); + await expect(editor.getByText('Analyzing the file')).toBeHidden(); }); test('it checks if callout custom block', async ({ page, browserName }) => { diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx index 879473f3d..8425caec7 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx @@ -33,7 +33,7 @@ import { useTranslation } from 'react-i18next'; import { WebsocketProvider } from 'y-websocket'; import * as Y from 'yjs'; -import { Box, TextErrors } from '@/components'; +import { Box } from '@/components'; import { useConfig } from '@/core'; import { useCunninghamTheme } from '@/cunningham'; import { @@ -133,7 +133,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { ? DEFAULT_LOCALE : i18n.resolvedLanguage; - const { uploadFile, errorAttachment } = useUploadFile(doc.id); + const { uploadFile } = useUploadFile(doc.id); const conf = useConfig().data; const { isFeatureFlagActivated } = useAnalytics(); const aiBlockNoteAllowed = !!( @@ -346,15 +346,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { currentUserAvatarUrl={currentUserAvatarUrl} /> - {errorAttachment && ( - - - - )} { isError: isErrorAttachment, error: errorAttachment, } = useCreateDocAttachment(); + const editor = useEditorStore((state) => state.editor); + const uploadBlockIdRef = useRef(undefined); const maxFileSize = config?.DOCUMENT_IMAGE_MAX_SIZE ?? DEFAULT_MAX_FILE_SIZE; const uploadFile = useCallback( - async (file: File) => { + async (file: File, blockId?: string) => { + uploadBlockIdRef.current = blockId; + // The server rejects an oversized file, but the proxy in front of it usually cuts the // request first and answers a bare 413 the editor cannot make sense of. Telling the // user before sending anything saves them the wait and the cryptic message. @@ -85,6 +90,38 @@ export const useUploadFile = (docId: string) => { [createDocAttachment, docId, maxFileSize, t, toast], ); + /** + * Handle the upload error by replacing the block with an + * uploadLoader block displaying a warning message. + */ + useEffect(() => { + const blockId = uploadBlockIdRef.current; + if (!errorAttachment || !blockId || !editor) { + return; + } + + try { + editor.replaceBlocks( + [blockId], + [ + { + type: 'uploadLoader', + props: { + information: t( + 'A problem occurred while uploading the file, please try again.', + ), + type: 'warning', + }, + }, + ], + ); + } catch { + /* The block may be gone already (removed or updated by a collaborator) */ + } + + uploadBlockIdRef.current = undefined; + }, [editor, errorAttachment, t]); + return { uploadFile, isErrorAttachment, @@ -132,7 +169,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => { { type: 'uploadLoader', props: { - information: t('Analyzing file...'), + information: t('Analyzing the file'), type: 'loading', blockUploadName, blockUploadType,