Skip to content

Commit aff00ca

Browse files
committed
🥅(frontend) change ui when catch unexpected upload error
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.
1 parent e76dc94 commit aff00ca

4 files changed

Lines changed: 57 additions & 31 deletions

File tree

‎src/frontend/apps/e2e/__tests__/app-impress/doc-editor.spec.ts‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -356,20 +356,20 @@ test.describe('Doc Editor', () => {
356356
const fileChooser = await fileChooserPromise;
357357
await fileChooser.setFiles(path.join(__dirname, 'assets/test.html'));
358358

359-
await expect(editor.getByText('Analyzing file...')).toBeVisible();
359+
await expect(editor.getByText('Analyzing the file')).toBeVisible();
360360

361361
// To be sure the retry happens even after a page reload
362362
await goToGridDoc(page, {
363363
title: randomDoc,
364364
});
365365

366-
await expect(editor.getByText('Analyzing file...')).toBeVisible();
366+
await expect(editor.getByText('Analyzing the file')).toBeVisible();
367367

368368
// The retry takes a few seconds
369369
await expect(editor.getByText('test.html')).toBeVisible({
370370
timeout: 7000,
371371
});
372-
await expect(editor.getByText('Analyzing file...')).toBeHidden();
372+
await expect(editor.getByText('Analyzing the file')).toBeHidden();
373373
});
374374

375375
test('it checks if callout custom block', async ({ page, browserName }) => {

‎src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteEditor.tsx‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@ import {
2323
import {
2424
FloatingComposerController,
2525
FloatingThreadController,
26+
ReactAudioBlock,
27+
ReactFileBlock,
28+
ReactImageBlock,
29+
ReactVideoBlock,
2630
ThreadsSidebar,
2731
useCreateBlockNote,
2832
} from '@blocknote/react';
@@ -33,7 +37,7 @@ import { useTranslation } from 'react-i18next';
3337
import { WebsocketProvider } from 'y-websocket';
3438
import * as Y from 'yjs';
3539

36-
import { Box, TextErrors } from '@/components';
40+
import { Box } from '@/components';
3741
import { useConfig } from '@/core';
3842
import { useCunninghamTheme } from '@/cunningham';
3943
import {
@@ -83,6 +87,10 @@ const baseBlockNoteSchema = withPageBreak(
8387
BlockNoteSchema.create({
8488
blockSpecs: {
8589
...defaultBlockSpecs,
90+
audio: ReactAudioBlock(),
91+
file: ReactFileBlock(),
92+
image: ReactImageBlock(),
93+
video: ReactVideoBlock(),
8694
callout: CalloutBlock(),
8795
codeBlock: createSafeCodeBlockSpec(),
8896
diagram: createReactDiagramBlockSpec(),
@@ -133,7 +141,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
133141
? DEFAULT_LOCALE
134142
: i18n.resolvedLanguage;
135143

136-
const { uploadFile, errorAttachment } = useUploadFile(doc.id);
144+
const { uploadFile } = useUploadFile(doc.id);
137145
const conf = useConfig().data;
138146
const { isFeatureFlagActivated } = useAnalytics();
139147
const aiBlockNoteAllowed = !!(
@@ -346,15 +354,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => {
346354
currentUserAvatarUrl={currentUserAvatarUrl}
347355
/>
348356
<DocsFindReplaceStyle />
349-
{errorAttachment && (
350-
<Box $margin={{ bottom: 'big', top: 'none', horizontal: 'large' }}>
351-
<TextErrors
352-
causes={errorAttachment.cause}
353-
canClose
354-
$textAlign="left"
355-
/>
356-
</Box>
357-
)}
358357
<BlockNoteView
359358
className="--docs--main-editor"
360359
editor={editor}

‎src/frontend/apps/impress/src/features/docs/doc-editor/hook/__tests__/useUploadFile.test.tsx‎

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,26 @@ describe('useUploadFile', () => {
7171
'error',
7272
);
7373
});
74+
75+
it('toasts the causes of the API and rethrows when the upload fails', async () => {
76+
fetchMock.hardReset();
77+
fetchMock.mockGlobal();
78+
fetchMock.post(uploadUrl, {
79+
status: 400,
80+
body: { file: ['File type not allowed.'] },
81+
});
82+
83+
const result = renderUseUploadFile();
84+
85+
await expect(
86+
result.current.uploadFile(createFile(MAX_FILE_SIZE)),
87+
).rejects.toThrow('Failed to upload on the doc');
88+
89+
expect(mockToast).toHaveBeenCalledWith(
90+
'A problem occurred while uploading the file, cause: File type not allowed.',
91+
'error',
92+
);
93+
});
7494
});
7595

7696
describe('useUploadStatus', () => {

‎src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx‎

Lines changed: 24 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { captureException } from '@sentry/nextjs';
44
import { useCallback, useEffect } from 'react';
55
import { useTranslation } from 'react-i18next';
66

7-
import { backendUrl } from '@/api';
7+
import { APIError, backendUrl } from '@/api';
88
import { useConfig } from '@/core';
99
import { useToast } from '@/hooks';
1010
import { formatFileSize } from '@/utils';
@@ -44,11 +44,7 @@ export const useUploadFile = (docId: string) => {
4444
const { t } = useTranslation();
4545
const { toast } = useToast();
4646
const { data: config } = useConfig();
47-
const {
48-
mutateAsync: createDocAttachment,
49-
isError: isErrorAttachment,
50-
error: errorAttachment,
51-
} = useCreateDocAttachment();
47+
const { mutateAsync: createDocAttachment } = useCreateDocAttachment();
5248

5349
const maxFileSize = config?.DOCUMENT_IMAGE_MAX_SIZE ?? DEFAULT_MAX_FILE_SIZE;
5450

@@ -75,21 +71,32 @@ export const useUploadFile = (docId: string) => {
7571
const body = new FormData();
7672
body.append('file', file);
7773

78-
const ret = await createDocAttachment({
79-
docId,
80-
body,
81-
});
74+
try {
75+
const ret = await createDocAttachment({
76+
docId,
77+
body,
78+
});
8279

83-
return `${backendUrl()}${ret.file}`;
80+
return `${backendUrl()}${ret.file}`;
81+
} catch (error) {
82+
const causes = error instanceof APIError ? error.cause : undefined;
83+
84+
toast(
85+
causes?.length
86+
? `${t('A problem occurred while uploading the file, cause:')} ${causes.join(' ')}`
87+
: t(
88+
'A problem occurred while uploading the file, please try again.',
89+
),
90+
VariantType.ERROR,
91+
);
92+
93+
throw error;
94+
}
8495
},
8596
[createDocAttachment, docId, maxFileSize, t, toast],
8697
);
8798

88-
return {
89-
uploadFile,
90-
isErrorAttachment,
91-
errorAttachment,
92-
};
99+
return { uploadFile };
93100
};
94101

95102
/**
@@ -132,7 +139,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => {
132139
{
133140
type: 'uploadLoader',
134141
props: {
135-
information: t('Analyzing file...'),
142+
information: t('Analyzing the file'),
136143
type: 'loading',
137144
blockUploadName,
138145
blockUploadType,

0 commit comments

Comments
 (0)