diff --git a/CHANGELOG.md b/CHANGELOG.md index b5e701ee64..d74a299a5b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -18,6 +18,8 @@ and this project adheres to ### Fixed - 🐛(frontend) paste content with comments from another document #1732 +- 🐛(frontend) Select text + Go back one page crash the app #1733 + ## [4.1.0] - 2025-12-09 diff --git a/src/frontend/apps/e2e/__tests__/app-impress/doc-comments.spec.ts b/src/frontend/apps/e2e/__tests__/app-impress/doc-comments.spec.ts index 347233e7fe..930b46d80c 100644 --- a/src/frontend/apps/e2e/__tests__/app-impress/doc-comments.spec.ts +++ b/src/frontend/apps/e2e/__tests__/app-impress/doc-comments.spec.ts @@ -6,7 +6,7 @@ import { getOtherBrowserName, verifyDocName, } from './utils-common'; -import { writeInEditor } from './utils-editor'; +import { getEditor, writeInEditor } from './utils-editor'; import { addNewMember, connectOtherUserToDoc, @@ -48,6 +48,7 @@ test.describe('Doc Comments', () => { await thread.locator('[data-test="save"]').click(); await expect(thread.getByText('This is a comment').first()).toBeHidden(); + await editor.first().click(); await editor.getByText('Hello').click(); await thread.getByText('This is a comment').first().hover(); @@ -135,6 +136,7 @@ test.describe('Doc Comments', () => { 'background-color', 'rgba(237, 180, 0, 0.4)', ); + await editor.first().click(); await editor.getByText('Hello').click(); await thread.getByText('This is a comment').first().hover(); @@ -197,6 +199,7 @@ test.describe('Doc Comments', () => { 'background-color', 'rgba(237, 180, 0, 0.4)', ); + await editor.first().click(); await editor.getByText('Hello').click(); await thread.getByText('This is a new comment').first().hover(); @@ -217,6 +220,8 @@ test.describe('Doc Comments', () => { // We share the doc with another user const otherBrowserName = getOtherBrowserName(browserName); + const editor = await getEditor({ page }); + // Add a new member with editor role await page.getByRole('button', { name: 'Share' }).click(); await addNewMember(page, 0, 'Editor', otherBrowserName); @@ -240,7 +245,7 @@ test.describe('Doc Comments', () => { text: 'Hello, I can edit the document', }); await expect( - otherEditor.getByText('Hello, I can edit the document'), + editor.getByText('Hello, I can edit the document'), ).toBeVisible(); await otherEditor.getByText('Hello').selectText(); await otherPage.getByRole('button', { name: 'Comment' }).click(); 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 cc981f73f4..4c355861aa 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 @@ -802,6 +802,8 @@ test.describe('Doc Editor', () => { await page.getByText('Symbols').scrollIntoViewIfNeeded(); await expect(page.getByRole('button', { name: '🛃' })).toBeVisible(); + await page.keyboard.press('Escape'); + await page.locator('.bn-side-menu > button').last().click(); await page.locator('.mantine-Menu-dropdown > button').last().click(); await page.locator('.bn-color-picker-dropdown > button').last().click(); diff --git a/src/frontend/apps/impress/package.json b/src/frontend/apps/impress/package.json index 675b71dbbb..f88ff58a99 100644 --- a/src/frontend/apps/impress/package.json +++ b/src/frontend/apps/impress/package.json @@ -19,14 +19,14 @@ }, "dependencies": { "@ag-media/react-pdf-table": "2.0.3", - "@blocknote/code-block": "0.42.3", - "@blocknote/core": "0.42.3", - "@blocknote/mantine": "0.42.3", - "@blocknote/react": "0.42.3", - "@blocknote/xl-docx-exporter": "0.42.3", - "@blocknote/xl-multi-column": "0.42.3", - "@blocknote/xl-odt-exporter": "0.42.3", - "@blocknote/xl-pdf-exporter": "0.42.3", + "@blocknote/code-block": "0.44.2", + "@blocknote/core": "0.44.2", + "@blocknote/mantine": "0.44.2", + "@blocknote/react": "0.44.2", + "@blocknote/xl-docx-exporter": "0.44.2", + "@blocknote/xl-multi-column": "0.44.2", + "@blocknote/xl-odt-exporter": "0.44.2", + "@blocknote/xl-pdf-exporter": "0.44.2", "@dnd-kit/core": "6.3.1", "@dnd-kit/modifiers": "9.0.0", "@emoji-mart/data": "1.2.1", 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 6ec70151cd..ed0dafddfa 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 @@ -6,6 +6,7 @@ import { defaultInlineContentSpecs, withPageBreak, } from '@blocknote/core'; +import { CommentsExtension } from '@blocknote/core/comments'; import '@blocknote/core/fonts/inter.css'; import * as locales from '@blocknote/core/locales'; import { BlockNoteView } from '@blocknote/mantine'; @@ -101,7 +102,11 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { const cursorName = collabName || t('Anonymous'); const showCursorLabels: 'always' | 'activity' | (string & {}) = 'activity'; - const threadStore = useComments(doc.id, canSeeComment, user); + const { resolveUsers, threadStore } = useComments( + doc.id, + canSeeComment, + user, + ); const currentUserAvatarUrl = useMemo(() => { if (canSeeComment) { @@ -156,7 +161,6 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { }, showCursorLabels: showCursorLabels as 'always' | 'activity', }, - comments: { threadStore }, dictionary: { ...locales[lang as keyof typeof locales], multi_column: @@ -182,22 +186,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { return defaultPasteHandler(); }, - resolveUsers: async (userIds) => { - return Promise.resolve( - userIds.map((encodedURIUserId) => { - const fullName = decodeURIComponent(encodedURIUserId); - - return { - id: encodedURIUserId, - username: fullName || t('Anonymous'), - avatarUrl: avatarUrlFromName( - fullName, - themeTokens?.font?.families?.base, - ), - }; - }), - ); - }, + extensions: [CommentsExtension({ threadStore, resolveUsers })], tables: { splitCells: true, cellBackgroundColor: true, @@ -207,7 +196,7 @@ export const BlockNoteEditor = ({ doc, provider }: BlockNoteEditorProps) => { uploadFile, schema: blockNoteSchema, }, - [cursorName, lang, provider, uploadFile, threadStore], + [cursorName, lang, provider, uploadFile, threadStore, resolveUsers], ); useHeadings(editor); @@ -268,7 +257,7 @@ export const BlockNoteReader = ({ }: BlockNoteReaderProps) => { const { user } = useAuth(); const { setEditor } = useEditorStore(); - const threadStore = useComments(docId, false, user); + const { threadStore } = useComments(docId, false, user); const { t } = useTranslation(); const editor = useCreateBlockNote( { @@ -281,12 +270,16 @@ export const BlockNoteReader = ({ provider: undefined, }, schema: blockNoteSchema, - comments: { threadStore }, - resolveUsers: async () => { - return Promise.resolve([]); - }, + extensions: [ + CommentsExtension({ + threadStore, + resolveUsers: async () => { + return Promise.resolve([]); + }, + }), + ], }, - [initialContent], + [initialContent, threadStore], ); useEffect(() => { diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx index badc40dcde..e07d15bc06 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteSuggestionMenu.tsx @@ -1,4 +1,5 @@ -import { combineByGroup, filterSuggestionItems } from '@blocknote/core'; +import { combineByGroup } from '@blocknote/core'; +import { filterSuggestionItems } from '@blocknote/core/extensions'; import { DefaultReactSuggestionItem, SuggestionMenuController, diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/FileDownloadButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/FileDownloadButton.tsx index abcc71db23..458a3c8491 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/FileDownloadButton.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/BlockNoteToolBar/FileDownloadButton.tsx @@ -6,24 +6,25 @@ * https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/FileDownloadButton.tsx */ -import { - BlockSchema, - InlineContentSchema, - StyleSchema, - blockHasType, -} from '@blocknote/core'; +import { blockHasType } from '@blocknote/core'; import { useBlockNoteEditor, useComponentsContext, useDictionary, - useSelectedBlocks, + useEditorState, } from '@blocknote/react'; -import { useCallback, useMemo } from 'react'; +import { useCallback } from 'react'; import { RiDownload2Fill } from 'react-icons/ri'; import { downloadFile, exportResolveFileUrl } from '@/docs/doc-export'; import { isSafeUrl } from '@/utils/url'; +import { + DocsBlockSchema, + DocsInlineContentSchema, + DocsStyleSchema, +} from '../../types'; + export const FileDownloadButton = ({ open, }: { @@ -33,36 +34,43 @@ export const FileDownloadButton = ({ const Components = useComponentsContext(); const editor = useBlockNoteEditor< - BlockSchema, - InlineContentSchema, - StyleSchema + DocsBlockSchema, + DocsInlineContentSchema, + DocsStyleSchema >(); - const selectedBlocks = useSelectedBlocks(editor); + const fileBlock = useEditorState({ + editor, + selector: ({ editor }) => { + const selectedBlocks = editor.getSelection()?.blocks || [ + editor.getTextCursorPosition().block, + ]; - const fileBlock = useMemo(() => { - // Checks if only one block is selected. - if (selectedBlocks.length !== 1) { - return undefined; - } + if (selectedBlocks.length !== 1) { + return undefined; + } - const block = selectedBlocks[0]; + const block = selectedBlocks[0]; - if ( - blockHasType(block, editor, block.type, { url: 'string', name: 'string' }) - ) { - return block; - } + if ( + !blockHasType(block, editor, block.type, { + url: 'string', + name: 'string', + }) + ) { + return undefined; + } - return undefined; - }, [editor, selectedBlocks]); + return block; + }, + }); const onClick = useCallback(async () => { if (fileBlock && fileBlock.props.url) { editor.focus(); const url = fileBlock.props.url as string; - const name = fileBlock.props.name as string | undefined; + const name = fileBlock.props.name as string; /** * If not hosted on our domain, means not a file uploaded by the user, @@ -110,19 +118,18 @@ export const FileDownloadButton = ({ return null; } + const blockType = fileBlock.type as string; + const tooltipText = + dict.formatting_toolbar.file_download.tooltip[blockType] || + dict.formatting_toolbar.file_download.tooltip['file']; + return ( <> } onClick={() => void onClick()} /> diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx index d8070664e7..ad49ebfa3c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/DocEditor.tsx @@ -90,7 +90,7 @@ export const DocEditor = ({ doc }: DocEditorProps) => { } }, [isProviderReady, setIsSkeletonVisible]); - if (!isProviderReady) { + if (!isProviderReady || provider?.configuration.name !== doc.id) { return ; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/CommentToolbarButton.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/CommentToolbarButton.tsx index 6fa3f2209d..ac2fd963ed 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/CommentToolbarButton.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/CommentToolbarButton.tsx @@ -3,9 +3,12 @@ * https://github.com/TypeCellOS/BlockNote/blob/main/packages/react/src/components/FormattingToolbar/DefaultButtons/AddCommentButton.tsx */ +import { CommentsExtension } from '@blocknote/core/comments'; +import { FormattingToolbarExtension } from '@blocknote/core/extensions'; import { useBlockNoteEditor, useComponentsContext, + useExtension, useSelectedBlocks, } from '@blocknote/react'; import { useMemo } from 'react'; @@ -29,6 +32,10 @@ export const CommentToolbarButton = () => { const { t } = useTranslation(); const { spacingsTokens, colorsTokens } = useCunninghamTheme(); const { isDesktop } = useResponsiveStore(); + const comments = useExtension('comments') as unknown as ReturnType< + ReturnType + >; + const { store } = useExtension(FormattingToolbarExtension); const editor = useBlockNoteEditor< DocsBlockSchema, @@ -53,6 +60,7 @@ export const CommentToolbarButton = () => { }; if ( + !comments || !isDesktop || !show || !editor.isEditable || @@ -67,8 +75,8 @@ export const CommentToolbarButton = () => { { - editor.comments?.startPendingComment(); - editor.formattingToolbar.closeMenu(); + comments.startPendingComment(); + store.setState(false); focusOnInputThread(); }} aria-haspopup="dialog" diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStore.tsx index b9b71bea5c..96a07d8951 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStore.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/DocsThreadStore.tsx @@ -200,7 +200,7 @@ export class DocsThreadStore extends ThreadStore { const { editor } = useEditorStore.getState(); // Should not happen - if (!editor) { + if (!editor || !editor._tiptapEditor?.view?.dom) { console.warn('Editor to add thread not ready'); return Promise.resolve(); } diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts index 99be3acfea..33a67a04da 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/comments/useComments.ts @@ -1,6 +1,8 @@ -import { useEffect, useMemo } from 'react'; +import { useCallback, useEffect, useMemo } from 'react'; +import { useTranslation } from 'react-i18next'; -import { User } from '@/features/auth'; +import { useCunninghamTheme } from '@/cunningham'; +import { User, avatarUrlFromName } from '@/features/auth'; import { Doc, useProviderStore } from '@/features/docs/doc-management'; import { DocsThreadStore } from './DocsThreadStore'; @@ -12,6 +14,9 @@ export function useComments( user: User | null | undefined, ) { const { provider } = useProviderStore(); + const { t } = useTranslation(); + const { themeTokens } = useCunninghamTheme(); + const threadStore = useMemo(() => { return new DocsThreadStore( docId, @@ -29,5 +34,25 @@ export function useComments( }; }, [threadStore]); - return threadStore; + const resolveUsers = useCallback( + async (userIds: string[]) => { + return Promise.resolve( + userIds.map((encodedURIUserId) => { + const fullName = decodeURIComponent(encodedURIUserId); + + return { + id: encodedURIUserId, + username: fullName || t('Anonymous'), + avatarUrl: avatarUrlFromName( + fullName, + themeTokens?.font?.families?.base, + ), + }; + }), + ); + }, + [t, themeTokens?.font?.families?.base], + ); + + return { threadStore, resolveUsers }; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx index 72999f3f7c..4a54ca9616 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/components/custom-blocks/CalloutBlock.tsx @@ -5,8 +5,8 @@ import { InlineContentSchema, StyleSchema, defaultProps, - insertOrUpdateBlock, } from '@blocknote/core'; +import { insertOrUpdateBlockForSlashMenu } from '@blocknote/core/extensions'; import { BlockTypeSelectItem, createReactBlockSpec } from '@blocknote/react'; import { TFunction } from 'i18next'; import React, { useEffect, useState } from 'react'; @@ -97,34 +97,41 @@ const CalloutComponent = ({ `} > - + { + if (e.key === 'Escape' && openEmojiPicker) { + setOpenEmojiPicker(false); + } + }} + $css={css` + font-size: 1.125rem; + cursor: ${isEditable ? 'pointer' : 'default'}; + ${isEditable && + ` &:hover { background-color: rgba(0, 0, 0, 0.1); } `} - `} - $align="center" - $width="28px" - $radius="4px" - > - {block.props.emoji} - - - {openEmojiPicker && ( - - )} + `} + $align="center" + $width="28px" + $radius="4px" + > + {block.props.emoji} + + + {openEmojiPicker && ( + + )} + ); @@ -156,7 +163,7 @@ export const getCalloutReactSlashMenuItems = ( key: 'callout', title: t('Callout'), onItemClick: () => { - insertOrUpdateBlock(editor, { + insertOrUpdateBlockForSlashMenu(editor, { type: 'callout', }); }, 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 ace3e738d7..539eee69ed 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 @@ -4,8 +4,8 @@ import { BlockNoteEditor, InlineContentSchema, StyleSchema, - insertOrUpdateBlock, } from '@blocknote/core'; +import { insertOrUpdateBlockForSlashMenu } from '@blocknote/core/extensions'; import * as locales from '@blocknote/core/locales'; import { AddFileButton, @@ -139,7 +139,7 @@ export const getPdfReactSlashMenuItems = ( { title: t('PDF'), onItemClick: () => { - insertOrUpdateBlock(editor, { type: 'pdf' }); + insertOrUpdateBlockForSlashMenu(editor, { type: 'pdf' }); }, aliases: [t('pdf'), t('document'), t('embed'), t('file')], group, diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useHeadings.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useHeadings.tsx index 6a0e11f7d3..b7f0172b6a 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useHeadings.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useHeadings.tsx @@ -8,7 +8,7 @@ export const useHeadings = (editor: DocsBlockNoteEditor) => { useEffect(() => { // Check if editor and its view are mounted before accessing document - if (!editor || !editor._tiptapEditor?.view?.dom) { + if (!editor) { return; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useShortcuts.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useShortcuts.tsx index 54f95c7ca5..09c8625d72 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useShortcuts.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/hook/useShortcuts.tsx @@ -8,7 +8,7 @@ export const useShortcuts = ( ) => { useEffect(() => { // Check if editor and its view are mounted - if (!editor || !editor._tiptapEditor?.view?.dom || !el) { + if (!editor || !el) { return; } 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 4487aa8df4..e2693b41ba 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 @@ -96,11 +96,11 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => { useEffect(() => { // Check if editor and its view are mounted before accessing document - if (!editor || !editor._tiptapEditor?.view?.dom) { + if (!editor?.document) { return; } - const imagesBlocks = editor?.document.filter( + const imagesBlocks = editor.document.filter( (block) => block.type === 'image' && block.props.url.includes(ANALYZE_URL), ); @@ -116,7 +116,7 @@ export const useUploadStatus = (editor: DocsBlockNoteEditor) => { */ useEffect(() => { // Check if editor and its view are mounted before setting up handlers - if (!editor || !editor._tiptapEditor?.view?.dom) { + if (!editor) { return; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx index a403c98425..defab77ec9 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-editor/stores/useHeadingStore.tsx @@ -29,7 +29,7 @@ export const useHeadingStore = create((set, get) => ({ headings: [], setHeadings: (editor) => { // Check if editor and its view are mounted before accessing document - if (!editor || !editor._tiptapEditor?.view?.dom) { + if (!editor?.document) { return; } diff --git a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx index 534f50502b..465d04a383 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-header/components/DocHeaderInfo.tsx @@ -3,15 +3,14 @@ import React from 'react'; import { Text } from '@/components'; import { useConfig } from '@/core'; -import { useDate } from '@/hook'; -import { useResponsiveStore } from '@/stores'; - import { Doc, Role, useIsCollaborativeEditable, useTrans, -} from '../../doc-management'; +} from '@/docs/doc-management'; +import { useDate } from '@/hook'; +import { useResponsiveStore } from '@/stores'; interface DocHeaderInfoProps { doc: Doc; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useDocTitleUpdate.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useDocTitleUpdate.tsx index 17c18ae96e..c63a90a2c0 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useDocTitleUpdate.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/hooks/useDocTitleUpdate.tsx @@ -1,15 +1,12 @@ import { useTreeContext } from '@gouvfr-lasuite/ui-kit'; import { useCallback } from 'react'; -import { - Doc, - KEY_DOC, - KEY_LIST_DOC, - getEmojiAndTitle, - useUpdateDoc, -} from '@/docs/doc-management'; import { useBroadcastStore } from '@/stores'; +import { KEY_DOC, KEY_LIST_DOC, useUpdateDoc } from '../api'; +import { Doc } from '../types'; +import { getEmojiAndTitle } from '../utils'; + interface UseDocUpdateOptions { onSuccess?: (updatedDoc: Doc) => void; onError?: (error: Error) => void; diff --git a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx index 33eb3d6ab1..f1c8d51184 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-management/stores/useProviderStore.tsx @@ -52,13 +52,29 @@ export const useProviderStore = create((set, get) => ({ } }, onAuthenticationFailed() { - set({ isReady: true }); + set({ isReady: true, isConnected: false }); + }, + onAuthenticated() { + set({ isReady: true, isConnected: true }); }, onStatus: ({ status }) => { set((state) => { const nextConnected = status === WebSocketStatus.Connected; + + /** + * status === WebSocketStatus.Connected does not mean we are totally connected + * because authentication can still be in progress and failed + * So we only update isConnected when we loose the connection + */ + const connected = + status !== WebSocketStatus.Connected + ? { + isConnected: false, + } + : undefined; + return { - isConnected: nextConnected, + ...connected, isReady: state.isReady || status === WebSocketStatus.Disconnected, hasLostConnection: state.isConnected && !nextConnected diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx index 89325c3060..dc47859d5c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocSubPageItem.tsx @@ -16,7 +16,7 @@ import { DocIcon, getEmojiAndTitle, useTrans, -} from '@/features/docs/doc-management'; +} from '@/docs/doc-management'; import { useLeftPanelStore } from '@/features/left-panel'; import { useResponsiveStore } from '@/stores'; diff --git a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx index 4ce70ef6a8..c80051587c 100644 --- a/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx +++ b/src/frontend/apps/impress/src/features/docs/doc-tree/components/DocTreeItemActions.tsx @@ -17,8 +17,8 @@ import { useCopyDocLink, useCreateChildDoc, useDocTitleUpdate, - useDuplicateDoc, } from '@/docs/doc-management'; +import { useDuplicateDoc } from '@/docs/doc-management/api'; import { useDetachDoc } from '../api/useDetach'; import MoveDocIcon from '../assets/doc-extract-bold.svg'; diff --git a/src/frontend/servers/y-provider/package.json b/src/frontend/servers/y-provider/package.json index a8e876a8c5..6cf5744613 100644 --- a/src/frontend/servers/y-provider/package.json +++ b/src/frontend/servers/y-provider/package.json @@ -16,7 +16,7 @@ "node": ">=22" }, "dependencies": { - "@blocknote/server-util": "0.42.3", + "@blocknote/server-util": "0.44.2", "@hocuspocus/server": "3.4.0", "@sentry/node": "10.26.0", "@sentry/profiling-node": "10.26.0", @@ -30,7 +30,7 @@ "yjs": "*" }, "devDependencies": { - "@blocknote/core": "0.42.3", + "@blocknote/core": "0.44.2", "@hocuspocus/provider": "3.4.0", "@types/cors": "2.8.19", "@types/express": "5.0.5", diff --git a/src/frontend/yarn.lock b/src/frontend/yarn.lock index 68762c9df0..e91c494da1 100644 --- a/src/frontend/yarn.lock +++ b/src/frontend/yarn.lock @@ -1135,12 +1135,12 @@ resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39" integrity sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw== -"@blocknote/code-block@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/code-block/-/code-block-0.42.3.tgz#7b1a3ed0b4f2d75835c422c04f52e824aa0845cf" - integrity sha512-kPdHABXJdH7lxB1Fxqg/bxWmtO/5y3REgRcuppEpCkrfXlwV+RPCChCU2jnMTUBe6CZyFQwum6/D9oUGVJKRqw== +"@blocknote/code-block@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/code-block/-/code-block-0.44.2.tgz#955b34b43ac64259a3defc4c17e31e3a0853ad12" + integrity sha512-46gv3EsUcuzWmdAO/JCNwZvIChtssMGjdGobfiHNg3LIjJvNaK2F+Oib4cA4YTpNerGN8TfUQaTCJIK3fTw3og== dependencies: - "@blocknote/core" "0.42.3" + "@blocknote/core" "0.44.2" "@shikijs/core" "^3.13.0" "@shikijs/engine-javascript" "^3.13.0" "@shikijs/langs" "^3.13.0" @@ -1148,18 +1148,19 @@ "@shikijs/themes" "^3.13.0" "@shikijs/types" "^3.13.0" -"@blocknote/core@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/core/-/core-0.42.3.tgz#2ac1654a04df65d4618440e520ad187d0c070169" - integrity sha512-wtZki6Gok5Ac9Ek6QTQztcDymstEQgVCisJwiUZTWXh8CD4UKfnIxM7C9+6eEnZMmQ8GNTvRf1HXFl+E4N78VA== +"@blocknote/core@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/core/-/core-0.44.2.tgz#7d3844030129f9793937cafebb2b151870bc70f4" + integrity sha512-PG51Ccue99x4kZtp/inkIkovr8XNviHZaOKAxuSaQowxVrLLrL599e+/GHeFar4OcJ1dcFkVBAgSwx3kYk4lJA== dependencies: "@emoji-mart/data" "^1.2.1" + "@handlewithcare/prosemirror-inputrules" "0.1.3" "@shikijs/types" "3.13.0" + "@tanstack/store" "0.7.7" "@tiptap/core" "^3.11.0" "@tiptap/extension-bold" "^3.7.2" "@tiptap/extension-code" "^3.7.2" "@tiptap/extension-gapcursor" "^3.7.2" - "@tiptap/extension-history" "^3.7.2" "@tiptap/extension-horizontal-rule" "^3.7.2" "@tiptap/extension-italic" "^3.7.2" "@tiptap/extension-link" "^3.7.2" @@ -1193,37 +1194,42 @@ y-protocols "^1.0.6" yjs "^13.6.27" -"@blocknote/mantine@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/mantine/-/mantine-0.42.3.tgz#3cd0b13e6ecb40b0b086a4877c2b2361a2fad266" - integrity sha512-xzLweZG1KfFoOp/aSHTXE10IrfEHnhDlP0C2Qt2eNO2IHHa7l8XZJpIGhCoVMsn0yylm91OSynNfTO7JkZZi8w== +"@blocknote/mantine@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/mantine/-/mantine-0.44.2.tgz#0dcbfc779ffe918732ef58acebf376eb1e5c1ef2" + integrity sha512-jH00WtAo+DNJ2YUxo9avHC9SCMq3CKHK/xWIEYpqo+LcMk1NdMfhgx08xypX7zB8GWWXoqyxFmlCEydM878Pzw== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/react" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/react" "0.44.2" react-icons "^5.5.0" -"@blocknote/react@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/react/-/react-0.42.3.tgz#c2ec737083e7341a18084c59de2021e64ad5f665" - integrity sha512-YnrQ1uyezDbaxYcFstWOJ2r8BMxqwwEc7QAhrEjCMEyBAiOxSCPnrM4/GE2mOgCS0Xa9wIp2LDoPQP2Syv+2EA== +"@blocknote/react@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/react/-/react-0.44.2.tgz#729987c1d3eb5598bdec1cfe602919d9899677f3" + integrity sha512-HRcgP1T8Mlog2IqlmbBnvPJLvrv+aGKax3y8yPLJs/4qRkP5lMpA+FkpYYHXSLy9OTcGZBC8i0xcwlJ9CUasTQ== dependencies: - "@blocknote/core" "0.42.3" + "@blocknote/core" "0.44.2" "@emoji-mart/data" "^1.2.1" "@floating-ui/react" "^0.27.16" + "@floating-ui/utils" "0.2.10" + "@tanstack/react-store" "0.7.7" "@tiptap/core" "^3.11.0" "@tiptap/pm" "^3.11.0" "@tiptap/react" "^3.11.0" + "@types/use-sync-external-store" "1.5.0" emoji-mart "^5.6.0" + fast-deep-equal "^3.1.3" lodash.merge "^4.6.2" react-icons "^5.5.0" + use-sync-external-store "1.6.0" -"@blocknote/server-util@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/server-util/-/server-util-0.42.3.tgz#113fc33cabc4e6a9fa776183182dfee6fef7e6ff" - integrity sha512-M+jtKeC2aHOYBp6GQ0YR19iv0/0f1HElrrnKwlaSPbwR6bw6tg+yb3yQkaJJioLTpd2X2Z/RwcEvxSJGnlZ81w== +"@blocknote/server-util@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/server-util/-/server-util-0.44.2.tgz#26807f6e6b0de50a49058824155afd5f79af8717" + integrity sha512-ndqDXDaFcq4HT3LZSL2KlDs1GO09BJXu2CtYhov5Z/6XrFIy8N7nM6T6cZDbSg/4lksJQnR2St615miURQ6NJQ== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/react" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/react" "0.44.2" "@tiptap/core" "^3.11.0" "@tiptap/pm" "^3.11.0" jsdom "^25.0.1" @@ -1231,24 +1237,24 @@ y-protocols "^1.0.6" yjs "^13.6.27" -"@blocknote/xl-docx-exporter@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/xl-docx-exporter/-/xl-docx-exporter-0.42.3.tgz#e276b1a22c34a2b5d0837606d7b96cc3acaba92e" - integrity sha512-VpotYcG+fQFzC2gtqTlBJDi0GKQQ6RygzeyzBBDGeMKSH3P72TDKVYVqN4Ert7HxXz41aCLGgtaf6x9zlox26g== +"@blocknote/xl-docx-exporter@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/xl-docx-exporter/-/xl-docx-exporter-0.44.2.tgz#d9ad71cfd696267cd288097324125c40379c3d69" + integrity sha512-g3PgJ5S3n1uz4GuL0kxv7CbCpsq8zwefrtTWbiz4KKDFagX2JUIlaLuQxGG8jCTL8xsd7AigS8ofrfWwZCJoaA== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/xl-multi-column" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/xl-multi-column" "0.44.2" buffer "^6.0.3" docx "^9.5.1" image-meta "^0.2.2" -"@blocknote/xl-multi-column@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/xl-multi-column/-/xl-multi-column-0.42.3.tgz#39fad4ef51f26c4af15423f44334de8edf06fe3c" - integrity sha512-7ylZYlOOVNMJ3u4C07yiE6qr04kcEYnxY3UfcFBSyV8H+N0LHGLFFJIz6JPKWvji4fu5lvbxXqv0IcGbCQ0/cA== +"@blocknote/xl-multi-column@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/xl-multi-column/-/xl-multi-column-0.44.2.tgz#b848fc3c4e4424141aeb2edd0a4c93d9fd57da1d" + integrity sha512-y68NJb84NFo0zS6s5o09NhAyfySBnea3h+vnVq474DqfQoCkQQnb2B6qielDUkx2VpxObahSdUN9P6bwpnk20g== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/react" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/react" "0.44.2" "@tiptap/core" "^3.11.0" prosemirror-model "^1.25.4" prosemirror-state "^1.4.4" @@ -1257,25 +1263,25 @@ prosemirror-view "^1.41.3" react-icons "^5.5.0" -"@blocknote/xl-odt-exporter@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/xl-odt-exporter/-/xl-odt-exporter-0.42.3.tgz#fee0f142ca8ae5ce8bb969f9de0f79e2a92e1c6a" - integrity sha512-wW1Zxd3Y14IG5X/mi0OBoGV/EFxeO5Alsd0HVsBo0imk+GLSKx2YCU02plUG5l8IOQOUeWBHamm4OT+7sgj9Ow== +"@blocknote/xl-odt-exporter@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/xl-odt-exporter/-/xl-odt-exporter-0.44.2.tgz#b44524a986668c9e6a1324e15c3a6d6765418f65" + integrity sha512-khsYAhoRMacn3y1FQb3IjUHG7imOJRs6L4QPKeDKn5JB9z85Fa4ARltfAmG+Pd0pvTPtVzLQFc65vuqjgGXE+g== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/xl-multi-column" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/xl-multi-column" "0.44.2" "@zip.js/zip.js" "^2.8.8" buffer "^6.0.3" image-meta "^0.2.2" -"@blocknote/xl-pdf-exporter@0.42.3": - version "0.42.3" - resolved "https://registry.yarnpkg.com/@blocknote/xl-pdf-exporter/-/xl-pdf-exporter-0.42.3.tgz#667c0c2756e8300f0dc134718e6d2833947b799f" - integrity sha512-ZPGVHovDWhwu++vkVzDEh6KOoHK6q8iLFo9fGLcQ8oKjNCJoBr344Z42AdMMxcoCDddQmC+5yqzUN8J/9xnE1Q== +"@blocknote/xl-pdf-exporter@0.44.2": + version "0.44.2" + resolved "https://registry.yarnpkg.com/@blocknote/xl-pdf-exporter/-/xl-pdf-exporter-0.44.2.tgz#595d2ad64e381d5cadf2a1e47855a691caf98465" + integrity sha512-aNUSELq3e4XIRd48hSBuUoc4BkTXKs9gUUHaLKLpUyBAnjJktPiR4fngT69YC6BzuPBO2jmi8cFQybvB9xNenQ== dependencies: - "@blocknote/core" "0.42.3" - "@blocknote/react" "0.42.3" - "@blocknote/xl-multi-column" "0.42.3" + "@blocknote/core" "0.44.2" + "@blocknote/react" "0.44.2" + "@blocknote/xl-multi-column" "0.44.2" "@react-pdf/renderer" "^4.3.0" buffer "^6.0.3" docx "^9.5.1" @@ -1775,7 +1781,7 @@ "@floating-ui/utils" "^0.2.10" tabbable "^6.0.0" -"@floating-ui/utils@^0.2.10": +"@floating-ui/utils@0.2.10", "@floating-ui/utils@^0.2.10": version "0.2.10" resolved "https://registry.yarnpkg.com/@floating-ui/utils/-/utils-0.2.10.tgz#a2a1e3812d14525f725d011a73eceb41fef5bc1c" integrity sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ== @@ -1887,6 +1893,14 @@ dependencies: is-negated-glob "^1.0.0" +"@handlewithcare/prosemirror-inputrules@0.1.3": + version "0.1.3" + resolved "https://registry.yarnpkg.com/@handlewithcare/prosemirror-inputrules/-/prosemirror-inputrules-0.1.3.tgz#77364764d9dfae115dbf2cbbfe684a3b87652ac7" + integrity sha512-LjGitwgSFHICeU6Mfbt+0Bp4BuWyvHfDYJIf7rq1qdNO88tFcWV3CSqw75o/YbsnUObDgp5Dn+gXIQLRwiyCbg== + dependencies: + prosemirror-history "^1.4.1" + prosemirror-transform "^1.0.0" + "@hocuspocus/common@^3.4.0": version "3.4.0" resolved "https://registry.yarnpkg.com/@hocuspocus/common/-/common-3.4.0.tgz#1e01ca39e23fb9335fae2057db9a0a44613abfa0" @@ -5641,6 +5655,14 @@ dependencies: "@tanstack/query-core" "5.90.10" +"@tanstack/react-store@0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@tanstack/react-store/-/react-store-0.7.7.tgz#6c51761956a1b3713ae0a8dbc008ea181f82df1f" + integrity sha512-qqT0ufegFRDGSof9D/VqaZgjNgp4tRPHZIJq2+QIHkMUtHjaJ0lYrrXjeIUJvjnTbgPfSD1XgOMEt0lmANn6Zg== + dependencies: + "@tanstack/store" "0.7.7" + use-sync-external-store "^1.5.0" + "@tanstack/react-table@8.21.3": version "8.21.3" resolved "https://registry.yarnpkg.com/@tanstack/react-table/-/react-table-8.21.3.tgz#2c38c747a5731c1a07174fda764b9c2b1fb5e91b" @@ -5648,6 +5670,11 @@ dependencies: "@tanstack/table-core" "8.21.3" +"@tanstack/store@0.7.7": + version "0.7.7" + resolved "https://registry.yarnpkg.com/@tanstack/store/-/store-0.7.7.tgz#2c8b1d8c094f3614ae4e0483253239abd0e14488" + integrity sha512-xa6pTan1bcaqYDS9BDpSiS63qa6EoDkPN9RsRaxHuDdVDNntzq3xNwR5YKTU/V3SkSyC9T4YVOPh2zRQN0nhIQ== + "@tanstack/table-core@8.21.3": version "8.21.3" resolved "https://registry.yarnpkg.com/@tanstack/table-core/-/table-core-8.21.3.tgz#2977727d8fc8dfa079112d9f4d4c019110f1732c" @@ -5723,11 +5750,6 @@ resolved "https://registry.yarnpkg.com/@tiptap/extension-gapcursor/-/extension-gapcursor-3.10.2.tgz#d9b66196cae2ebfe7e1747a67b21713463832697" integrity sha512-sBCu8enXm3W3BjnvvGBrzAiSuQSVZyhbQAgaFKjHJKBQRbek55EEbRA0ETUmHcSQbYf0D8hmDt2++HAyEASEsQ== -"@tiptap/extension-history@^3.7.2": - version "3.10.2" - resolved "https://registry.yarnpkg.com/@tiptap/extension-history/-/extension-history-3.10.2.tgz#f19073e57f4bb8c2e5578c8ac0d7e71ae59d79be" - integrity sha512-1u65sQt0vAyXDOyA2YRgyMcPv6pEt60JEU3IOlt1flVYbIcTFy9X8FILmXlq5MC+bRyJXWn7SfjnJWhWbVv7zA== - "@tiptap/extension-horizontal-rule@^3.7.2": version "3.10.2" resolved "https://registry.yarnpkg.com/@tiptap/extension-horizontal-rule/-/extension-horizontal-rule-3.10.2.tgz#4250f704bb5da0e9c05f0930cd3adc63571a7066" @@ -6273,6 +6295,11 @@ resolved "https://registry.yarnpkg.com/@types/unist/-/unist-3.0.3.tgz#acaab0f919ce69cce629c2d4ed2eb4adc1b6c20c" integrity sha512-ko/gIFJRv177XgZsZcBwnqJN5x/Gien8qNOn0D5bQU/zAzVf9Zt3BlcUiLqhV9y4ARk0GbT3tnUiPNgnTXzc/Q== +"@types/use-sync-external-store@1.5.0": + version "1.5.0" + resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-1.5.0.tgz#222c28a98eb8f4f8a72c1a7e9fe6d8946eca6383" + integrity sha512-5dyB8nLC/qogMrlCizZnYWQTA4lnb/v+It+sqNl5YnSRAPMlIqY/X0Xn+gZw8vOL+TgTTr28VEbn3uf8fUtAkw== + "@types/use-sync-external-store@^0.0.6": version "0.0.6" resolved "https://registry.yarnpkg.com/@types/use-sync-external-store/-/use-sync-external-store-0.0.6.tgz#60be8d21baab8c305132eb9cb912ed497852aadc" @@ -15333,7 +15360,7 @@ use-sidecar@^1.1.3: detect-node-es "^1.1.0" tslib "^2.0.0" -use-sync-external-store@^1.2.0, use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0, use-sync-external-store@^1.6.0: +use-sync-external-store@1.6.0, use-sync-external-store@^1.2.0, use-sync-external-store@^1.2.2, use-sync-external-store@^1.4.0, use-sync-external-store@^1.5.0, use-sync-external-store@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz#b174bfa65cb2b526732d9f2ac0a408027876f32d" integrity sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==