diff --git a/CHANGELOG.md b/CHANGELOG.md index 048933fb..26ce160d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,10 @@ and this project adheres to - 🥅(frontend) intercept 401 error on GET threads #1754 +## Changed + +- 🦺(frontend) check content type pdf on PdfBlock #1756 + ### Fixed - 🐛(frontend) fix tables deletion #1752 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 ef4936c9..612a82e7 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 @@ -960,13 +960,35 @@ test.describe('Doc Editor', () => { test('it embeds PDF', async ({ page, browserName }) => { await createDoc(page, 'doc-toolbar', browserName, 1); + await page.getByRole('button', { name: 'Share' }).click(); + await updateShareLink(page, 'Public', 'Reading'); + + await page.getByRole('button', { name: 'Close the share modal' }).click(); + await openSuggestionMenu({ page }); await page.getByText('Embed a PDF file').click(); - const pdfBlock = page.locator('div[data-content-type="pdf"]').first(); + const pdfBlock = page.locator('div[data-content-type="pdf"]').last(); await expect(pdfBlock).toBeVisible(); + // Try with invalid PDF first + await page.getByText(/Add (PDF|file)/).click(); + + await page.locator('[data-test="embed-tab"]').click(); + + await page + .locator('[data-test="embed-input"]') + .fill('https://example.test/test.test'); + + await page.locator('[data-test="embed-input-button"]').click(); + + await expect(page.getByText('Invalid or missing PDF file')).toBeVisible(); + + await openSuggestionMenu({ page }); + await page.getByText('Embed a PDF file').click(); + + // Now with a valid PDF await page.getByText(/Add (PDF|file)/).click(); const fileChooserPromise = page.waitForEvent('filechooser'); const downloadPromise = page.waitForEvent('download'); @@ -991,7 +1013,7 @@ test.describe('Doc Editor', () => { await expect(pdfEmbed).toHaveAttribute('role', 'presentation'); // Check download with original filename - await page.locator('.bn-block-content[data-content-type="pdf"]').click(); + await pdfBlock.click(); await page.locator('[data-test="downloadfile"]').click(); const download = await downloadPromise; diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx index 539eee69..1130d2b5 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/PdfBlock.tsx @@ -13,12 +13,13 @@ import { createReactBlockSpec, } from '@blocknote/react'; import { TFunction } from 'i18next'; -import { useEffect } from 'react'; +import { useEffect, useState } from 'react'; import { useTranslation } from 'react-i18next'; -import { createGlobalStyle } from 'styled-components'; +import { createGlobalStyle, css } from 'styled-components'; -import { Box, Icon } from '@/components'; +import { Box, Icon, Loading } from '@/components'; +import { ANALYZE_URL } from '../../conf'; import { DocsBlockNoteEditor } from '../../types'; const PDFBlockStyle = createGlobalStyle` @@ -66,6 +67,9 @@ const PdfBlockComponent = ({ const pdfUrl = block.props.url; const { i18n, t } = useTranslation(); const lang = i18n.resolvedLanguage; + const [isPDFContent, setIsPDFContent] = useState(null); + const [isPDFContentLoading, setIsPDFContentLoading] = + useState(false); useEffect(() => { if (lang && locales[lang as keyof typeof locales]) { @@ -82,9 +86,55 @@ const PdfBlockComponent = ({ } }, [lang, t]); + useEffect(() => { + if (!pdfUrl || pdfUrl.includes(ANALYZE_URL)) { + return; + } + + const validatePDFContent = async () => { + setIsPDFContentLoading(true); + try { + const response = await fetch(pdfUrl, { + credentials: 'include', + }); + const contentType = response.headers.get('content-type'); + + if (response.ok && contentType?.includes('application/pdf')) { + setIsPDFContent(true); + } else { + setIsPDFContent(false); + } + } catch { + setIsPDFContent(false); + } finally { + setIsPDFContentLoading(false); + } + }; + + void validatePDFContent(); + }, [pdfUrl]); + return ( + {isPDFContentLoading && } + {!isPDFContentLoading && isPDFContent !== null && !isPDFContent && ( + editor.setTextCursorPosition(block)} + > + {t('Invalid or missing PDF file.')} + + )} @@ -92,18 +142,21 @@ const PdfBlockComponent = ({ block={block as unknown as FileBlockBlock} editor={editor as unknown as FileBlockEditor} > - editor.setTextCursorPosition(block)} - /> + {!isPDFContentLoading && isPDFContent && ( + editor.setTextCursorPosition(block)} + /> + )} ); diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/conf.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/conf.ts new file mode 100644 index 00000000..f78c1089 --- /dev/null +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/conf.ts @@ -0,0 +1 @@ +export const ANALYZE_URL = 'media-check'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx index e2693b41..2687bad9 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useUploadFile.tsx @@ -6,6 +6,7 @@ import { useTranslation } from 'react-i18next'; import { backendUrl } from '@/api'; import { useCreateDocAttachment } from '../api'; +import { ANALYZE_URL } from '../conf'; import { DocsBlockNoteEditor } from '../types'; export const useUploadFile = (docId: string) => { @@ -46,7 +47,6 @@ export const useUploadFile = (docId: string) => { * @param editor */ export const useUploadStatus = (editor: DocsBlockNoteEditor) => { - const ANALYZE_URL = 'media-check'; const { t } = useTranslation(); /**