From f4524149524eb6a70e1f72c078cec8fc2f7604b0 Mon Sep 17 00:00:00 2001 From: JimmFly Date: Thu, 5 Sep 2024 07:14:23 +0000 Subject: [PATCH] fix(core): hide the footer that blocks the toolbar in shared page (#8091) close PD-1405 CLOUD-64 https://github.com/user-attachments/assets/f6ed2dfc-d238-41d8-abaf-684193a080ff --- .../blocksuite-editor-container.tsx | 40 +--- .../block-suite-editor/blocksuite-editor.tsx | 187 ++++++++++-------- .../present/detail-header-present-button.tsx | 8 +- .../block-suite-header/present/use-present.ts | 59 ------ .../cloud/share-header-right-item/present.tsx | 8 +- .../share-header-right-item/user-avatar.tsx | 4 +- .../src/components/page-detail-editor.tsx | 55 +----- .../src/modules/editor/entities/editor.ts | 69 +++++-- .../view/doc-preview/doc-peek-view.tsx | 96 ++++----- .../workspace/detail-page/detail-page.tsx | 80 +------- .../src/pages/workspace/share/share-page.tsx | 77 +++++--- 11 files changed, 279 insertions(+), 404 deletions(-) delete mode 100644 packages/frontend/core/src/components/blocksuite/block-suite-header/present/use-present.ts diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx index ac3f66616b..348ac8ba0a 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx @@ -1,4 +1,3 @@ -import { EditorService } from '@affine/core/modules/editor'; import type { ReferenceInfo } from '@blocksuite/affine-model'; import type { DocMode } from '@blocksuite/blocks'; import type { @@ -8,13 +7,12 @@ import type { PageEditor, } from '@blocksuite/presets'; import { type Doc, Slot } from '@blocksuite/store'; -import { useService } from '@toeverything/infra'; import clsx from 'clsx'; import type React from 'react'; import { forwardRef, useCallback, - useEffect, + useImperativeHandle, useLayoutEffect, useMemo, useRef, @@ -60,7 +58,6 @@ export const BlocksuiteEditorContainer = forwardRef< { page, mode, className, style, shared }, ref ) { - const editorService = useService(EditorService); const rootRef = useRef(null); const docRef = useRef(null); const docTitleRef = useRef(null); @@ -112,6 +109,9 @@ export const BlocksuiteEditorContainer = forwardRef< get doc() { return page; }, + get docTitle() { + return docTitleRef.current; + }, get host() { return mode === 'page' ? docRef.current?.host @@ -159,35 +159,9 @@ export const BlocksuiteEditorContainer = forwardRef< return proxy; }, [mode, page, slots]); - useEffect(() => { - if (ref) { - if (typeof ref === 'function') { - ref(affineEditorContainerProxy); - } else { - ref.current = affineEditorContainerProxy; - } - } - }, [affineEditorContainerProxy, ref]); - - useEffect(() => { - let canceled = false; - let unsubscribe: () => void = () => {}; - - affineEditorContainerProxy.updateComplete - .then(() => { - if (!canceled) { - unsubscribe = editorService.editor.bindEditorContainer( - affineEditorContainerProxy, - docTitleRef.current as DocTitle - ); - } - }) - .catch(console.error); - return () => { - canceled = true; - unsubscribe(); - }; - }, [affineEditorContainerProxy, mode, editorService]); + useImperativeHandle(ref, () => affineEditorContainerProxy, [ + affineEditorContainerProxy, + ]); const handleClickPageModeBlank = useCallback(() => { affineEditorContainerProxy.host?.std.command.exec( diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor.tsx index aa1fcdd795..a92e2707cb 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor.tsx @@ -1,32 +1,30 @@ +import { useRefEffect } from '@affine/component'; import { EditorLoading } from '@affine/component/page-detail-skeleton'; -import type { DocMode } from '@blocksuite/blocks'; -import { assertExists } from '@blocksuite/global/utils'; +import { + BookmarkBlockService, + customImageProxyMiddleware, + type DocMode, + EmbedGithubBlockService, + EmbedLoomBlockService, + EmbedYoutubeBlockService, + ImageBlockService, +} from '@blocksuite/blocks'; +import { DisposableGroup } from '@blocksuite/global/utils'; import type { AffineEditorContainer } from '@blocksuite/presets'; import type { Doc } from '@blocksuite/store'; import { use } from 'foxact/use'; -import type { CSSProperties, ReactElement } from 'react'; -import { - forwardRef, - memo, - Suspense, - useCallback, - useEffect, - useRef, -} from 'react'; +import type { CSSProperties } from 'react'; +import { Suspense, useEffect } from 'react'; import { BlocksuiteEditorContainer } from './blocksuite-editor-container'; import { NoPageRootError } from './no-page-error'; -export type ErrorBoundaryProps = { - onReset?: () => void; -}; - export type EditorProps = { page: Doc; mode: DocMode; shared?: boolean; - // on Editor instance instantiated - onLoadEditor?: (editor: AffineEditorContainer) => () => void; + // on Editor ready + onEditorReady?: (editor: AffineEditorContainer) => (() => void) | void; style?: CSSProperties; className?: string; }; @@ -53,73 +51,100 @@ function usePageRoot(page: Doc) { return page.root; } -const BlockSuiteEditorImpl = forwardRef( - function BlockSuiteEditorImpl( - { mode, page, className, onLoadEditor, shared, style }, - ref - ) { - usePageRoot(page); - assertExists(page, 'page should not be null'); - const editorDisposeRef = useRef<() => void>(() => {}); - const editorRef = useRef(null); +const BlockSuiteEditorImpl = ({ + mode, + page, + className, + shared, + style, + onEditorReady, +}: EditorProps) => { + usePageRoot(page); - const onRefChange = useCallback( - (editor: AffineEditorContainer | null) => { - editorRef.current = editor; - if (ref) { - if (typeof ref === 'function') { - ref(editor); - } else { - ref.current = editor; - } - } - if (editor && onLoadEditor) { - editorDisposeRef.current = onLoadEditor(editor); - } - }, - [onLoadEditor, ref] - ); - - useEffect(() => { - const disposable = page.slots.blockUpdated.once(() => { - page.collection.setDocMeta(page.id, { - updatedDate: Date.now(), - }); + useEffect(() => { + const disposable = page.slots.blockUpdated.once(() => { + page.collection.setDocMeta(page.id, { + updatedDate: Date.now(), }); + }); + return () => { + disposable.dispose(); + }; + }, [page]); + + const editorRef = useRefEffect( + (editor: AffineEditorContainer) => { + globalThis.currentEditor = editor; + let canceled = false; + const disposableGroup = new DisposableGroup(); + + if (onEditorReady) { + // Invoke onLoad once the editor has been mounted to the DOM. + editor.updateComplete + .then(() => { + if (canceled) { + return; + } + // host should be ready + + // provide image proxy endpoint to blocksuite + editor.host?.std.clipboard.use( + customImageProxyMiddleware(runtimeConfig.imageProxyUrl) + ); + ImageBlockService.setImageProxyURL(runtimeConfig.imageProxyUrl); + + // provide link preview endpoint to blocksuite + BookmarkBlockService.setLinkPreviewEndpoint( + runtimeConfig.linkPreviewUrl + ); + EmbedGithubBlockService.setLinkPreviewEndpoint( + runtimeConfig.linkPreviewUrl + ); + EmbedYoutubeBlockService.setLinkPreviewEndpoint( + runtimeConfig.linkPreviewUrl + ); + EmbedLoomBlockService.setLinkPreviewEndpoint( + runtimeConfig.linkPreviewUrl + ); + + return editor.host?.updateComplete; + }) + .then(() => { + if (canceled) { + return; + } + const dispose = onEditorReady(editor); + if (dispose) { + disposableGroup.add(dispose); + } + }) + .catch(console.error); + } + return () => { - disposable.dispose(); + canceled = true; + disposableGroup.dispose(); }; - }, [page]); + }, + [onEditorReady, page] + ); - useEffect(() => { - return () => { - editorDisposeRef.current(); - }; - }, []); + return ( + + ); +}; - return ( - - ); - } -); - -export const BlockSuiteEditor = memo( - forwardRef( - function BlockSuiteEditor(props, ref): ReactElement { - return ( - }> - - - ); - } - ) -); - -BlockSuiteEditor.displayName = 'BlockSuiteEditor'; +export const BlockSuiteEditor = (props: EditorProps) => { + return ( + }> + + + ); +}; diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/present/detail-header-present-button.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-header/present/detail-header-present-button.tsx index 55fd0e1a3b..dc92907705 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/present/detail-header-present-button.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-header/present/detail-header-present-button.tsx @@ -1,16 +1,16 @@ import { IconButton } from '@affine/component'; +import { EditorService } from '@affine/core/modules/editor'; import { PresentationIcon } from '@blocksuite/icons/rc'; - -import { usePresent } from './use-present'; +import { useService } from '@toeverything/infra'; export const DetailPageHeaderPresentButton = () => { - const { isPresent, handlePresent } = usePresent(); + const editorService = useService(EditorService); return ( handlePresent(!isPresent)} + onClick={() => editorService.editor.togglePresentation()} > diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-header/present/use-present.ts b/packages/frontend/core/src/components/blocksuite/block-suite-header/present/use-present.ts deleted file mode 100644 index 411c478bfc..0000000000 --- a/packages/frontend/core/src/components/blocksuite/block-suite-header/present/use-present.ts +++ /dev/null @@ -1,59 +0,0 @@ -import { useActiveBlocksuiteEditor } from '@affine/core/hooks/use-block-suite-editor'; -import type { EdgelessRootService } from '@blocksuite/blocks'; -import { useCallback, useEffect, useState } from 'react'; - -export const usePresent = () => { - const [isPresent, setIsPresent] = useState(false); - const [editor] = useActiveBlocksuiteEditor(); - - const handlePresent = useCallback( - (enable = true) => { - isPresent; - const editorHost = editor?.host; - if (!editorHost) return; - - // TODO(@catsjuice): use surfaceService subAtom - const enterOrLeavePresentationMode = () => { - const edgelessRootService = editorHost.std.getService( - 'affine:page' - ) as EdgelessRootService; - - if (!edgelessRootService) { - return; - } - - const activeTool = edgelessRootService.tool.edgelessTool.type; - const isFrameNavigator = activeTool === 'frameNavigator'; - if ((enable && isFrameNavigator) || (!enable && !isFrameNavigator)) - return; - - edgelessRootService.tool.setEdgelessTool({ - type: enable ? 'frameNavigator' : 'default', - }); - }; - - enterOrLeavePresentationMode(); - setIsPresent(enable); - }, - [editor?.host, isPresent] - ); - - useEffect(() => { - if (!isPresent) return; - - const editorHost = editor?.host; - if (!editorHost) return; - - const edgelessPage = editorHost?.querySelector('affine-edgeless-root'); - if (!edgelessPage) return; - - return edgelessPage.slots.edgelessToolUpdated.on(() => { - setIsPresent(edgelessPage.edgelessTool.type === 'frameNavigator'); - }).dispose; - }, [editor?.host, isPresent]); - - return { - isPresent, - handlePresent, - }; -}; diff --git a/packages/frontend/core/src/components/cloud/share-header-right-item/present.tsx b/packages/frontend/core/src/components/cloud/share-header-right-item/present.tsx index 6c8c75a265..bd66d5bd79 100644 --- a/packages/frontend/core/src/components/cloud/share-header-right-item/present.tsx +++ b/packages/frontend/core/src/components/cloud/share-header-right-item/present.tsx @@ -1,19 +1,21 @@ import { Button } from '@affine/component/ui/button'; +import { EditorService } from '@affine/core/modules/editor'; import { useI18n } from '@affine/i18n'; import { PresentationIcon } from '@blocksuite/icons/rc'; +import { useLiveData, useService } from '@toeverything/infra'; -import { usePresent } from '../../blocksuite/block-suite-header/present/use-present'; import * as styles from './styles.css'; export const PresentButton = () => { const t = useI18n(); - const { isPresent, handlePresent } = usePresent(); + const editorService = useService(EditorService); + const isPresent = useLiveData(editorService.editor.isPresenting$); return (