diff --git a/apps/storybook/src/stories/block-suite-editor.stories.tsx b/apps/storybook/src/stories/block-suite-editor.stories.tsx index 21309b87df..14e28eb6dc 100644 --- a/apps/storybook/src/stories/block-suite-editor.stories.tsx +++ b/apps/storybook/src/stories/block-suite-editor.stories.tsx @@ -1,6 +1,8 @@ /* deepscan-disable USELESS_ARROW_FUNC_BIND */ +import { BlockHubWrapper } from '@affine/component/block-hub'; import type { EditorProps } from '@affine/component/block-suite-editor'; import { BlockSuiteEditor } from '@affine/component/block-suite-editor'; +import { rootBlockHubAtom } from '@affine/workspace/atom'; import { __unstableSchemas, AffineSchemas } from '@blocksuite/blocks/models'; import type { EditorContainer } from '@blocksuite/editor'; import type { Page } from '@blocksuite/store'; @@ -54,13 +56,13 @@ const Template: StoryFn = (props: Partial) => { }} > -
); diff --git a/apps/storybook/src/stories/image-preview-modal.stories.tsx b/apps/storybook/src/stories/image-preview-modal.stories.tsx index a4c05bcef3..f2fd66b957 100644 --- a/apps/storybook/src/stories/image-preview-modal.stories.tsx +++ b/apps/storybook/src/stories/image-preview-modal.stories.tsx @@ -1,7 +1,9 @@ +import { BlockHubWrapper } from '@affine/component/block-hub'; import { BlockSuiteEditor } from '@affine/component/block-suite-editor'; import { ImagePreviewModal } from '@affine/component/image-preview-modal'; import { initEmptyPage } from '@affine/env/blocksuite'; import { WorkspaceFlavour } from '@affine/env/workspace'; +import { rootBlockHubAtom } from '@affine/workspace/atom'; import { createEmptyBlockSuiteWorkspace } from '@affine/workspace/utils'; import type { Meta } from '@storybook/react'; @@ -54,13 +56,13 @@ export const Default = () => { > -
); diff --git a/apps/web/src/components/page-detail-editor.tsx b/apps/web/src/components/page-detail-editor.tsx index 053b58e2bc..79a7ac3b17 100644 --- a/apps/web/src/components/page-detail-editor.tsx +++ b/apps/web/src/components/page-detail-editor.tsx @@ -4,7 +4,7 @@ import { DEFAULT_HELLO_WORLD_PAGE_ID, PageNotFoundError, } from '@affine/env/constant'; -import { rootCurrentEditorAtom } from '@affine/workspace/atom'; +import { rootBlockHubAtom } from '@affine/workspace/atom'; import type { EditorContainer } from '@blocksuite/editor'; import { assertExists } from '@blocksuite/global/utils'; import type { Page } from '@blocksuite/store'; @@ -22,13 +22,7 @@ import clsx from 'clsx'; import { useAtomValue, useSetAtom } from 'jotai'; import Head from 'next/head'; import type { FC, ReactElement } from 'react'; -import React, { - memo, - startTransition, - Suspense, - useCallback, - useMemo, -} from 'react'; +import React, { memo, Suspense, useCallback, useMemo } from 'react'; import { Panel, PanelGroup, PanelResizeHandle } from 'react-resizable-panels'; import { pageSettingFamily } from '../atoms'; @@ -73,7 +67,7 @@ const EditorWrapper = memo(function EditorWrapper({ pageSetting?.mode ?? (DEFAULT_HELLO_WORLD_PAGE_ID === pageId ? 'edgeless' : 'page'); - const setEditor = useSetAtom(rootCurrentEditorAtom); + const setBlockHub = useSetAtom(rootBlockHubAtom); const [appSettings] = useAppSetting(); assertExists(meta); @@ -88,18 +82,13 @@ const EditorWrapper = memo(function EditorWrapper({ page={page} onInit={useCallback( (page: Page, editor: Readonly) => { - startTransition(() => { - setEditor(editor); - }); onInit(page, editor); }, - [onInit, setEditor] + [onInit] )} + setBlockHub={setBlockHub} onLoad={useCallback( (page: Page, editor: EditorContainer) => { - startTransition(() => { - setEditor(editor); - }); page.workspace.setPageMeta(page.id, { updatedDate: Date.now(), }); @@ -119,7 +108,7 @@ const EditorWrapper = memo(function EditorWrapper({ dispose(); }; }, - [plugins, onLoad, setEditor] + [plugins, onLoad] )} /> ); diff --git a/apps/web/src/layouts/workspace-layout.tsx b/apps/web/src/layouts/workspace-layout.tsx index 45c4c1ecd1..2bdf16b013 100644 --- a/apps/web/src/layouts/workspace-layout.tsx +++ b/apps/web/src/layouts/workspace-layout.tsx @@ -1,6 +1,7 @@ import { Content, displayFlex } from '@affine/component'; import { AffineWatermark } from '@affine/component/affine-watermark'; import { appSidebarResizingAtom } from '@affine/component/app-sidebar'; +import { BlockHubWrapper } from '@affine/component/block-hub'; import { NotificationCenter } from '@affine/component/notification-center'; import type { DraggableTitleCellData } from '@affine/component/page-list'; import { StyledTitleLink } from '@affine/component/page-list'; @@ -13,6 +14,7 @@ import { initEmptyPage, initPageWithPreloading } from '@affine/env/blocksuite'; import { DEFAULT_HELLO_WORLD_PAGE_ID, isDesktop } from '@affine/env/constant'; import { useAFFiNEI18N } from '@affine/i18n/hooks'; import { + rootBlockHubAtom, rootCurrentPageIdAtom, rootCurrentWorkspaceIdAtom, rootWorkspacesMetadataAtom, @@ -432,10 +434,7 @@ export const WorkspaceLayoutInner: FC = ({ children }) => { {children} - {/* fixme(himself65): remove this */} -
- {/* Slot for block hub */} -
+ {!isPublicWorkspace && ( { -
+ ); diff --git a/packages/component/src/components/block-hub/index.tsx b/packages/component/src/components/block-hub/index.tsx new file mode 100644 index 0000000000..afa14a8e7a --- /dev/null +++ b/packages/component/src/components/block-hub/index.tsx @@ -0,0 +1,25 @@ +import type { BlockHub } from '@blocksuite/blocks'; +import type { Atom } from 'jotai'; +import { useAtomValue } from 'jotai'; +import type { HTMLAttributes, ReactElement } from 'react'; +import { useRef } from 'react'; + +export interface BlockHubProps extends HTMLAttributes { + blockHubAtom: Atom | null>; +} + +export const BlockHubWrapper = (props: BlockHubProps): ReactElement => { + const blockHub = useAtomValue(props.blockHubAtom); + const ref = useRef(null); + if (ref.current) { + const div = ref.current; + if (!blockHub) { + if (div.hasChildNodes()) { + div.removeChild(div.firstChild as ChildNode); + } + } else { + div.appendChild(blockHub); + } + } + return
; +}; diff --git a/packages/component/src/components/block-suite-editor/index.tsx b/packages/component/src/components/block-suite-editor/index.tsx index 99f09232ad..dd649a390d 100644 --- a/packages/component/src/components/block-suite-editor/index.tsx +++ b/packages/component/src/components/block-suite-editor/index.tsx @@ -28,6 +28,7 @@ export type EditorProps = { page: Page; mode: 'page' | 'edgeless'; onInit: (page: Page, editor: Readonly) => void; + setBlockHub?: (blockHub: BlockHub | null) => void; onLoad?: (page: Page, editor: EditorContainer) => () => void; style?: CSSProperties; className?: string; @@ -96,6 +97,8 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => { const ref = useRef(null); + const setBlockHub = props.setBlockHub; + useEffect(() => { const editor = editorRef.current; assertExists(editor); @@ -111,13 +114,8 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => { blockHubRef.current.remove(); } blockHubRef.current = blockHub; - const toolWrapper = document.querySelector('#toolWrapper'); - if (!toolWrapper) { - console.warn( - 'toolWrapper not found, block hub feature will not be available.' - ); - } else { - toolWrapper.appendChild(blockHub); + if (setBlockHub) { + setBlockHub(blockHub); } }) .catch(err => { @@ -127,10 +125,13 @@ const BlockSuiteEditorImpl = (props: EditorProps): ReactElement => { container.appendChild(editor); return () => { + if (setBlockHub) { + setBlockHub(null); + } blockHubRef.current?.remove(); container.removeChild(editor); }; - }, [editor, page]); + }, [editor, setBlockHub, page]); // issue: https://github.com/toeverything/AFFiNE/issues/2004 const className = `editor-wrapper ${editor.mode}-mode ${ diff --git a/packages/workspace/src/atom.ts b/packages/workspace/src/atom.ts index ee6ee9bf9a..b0c9167cd4 100644 --- a/packages/workspace/src/atom.ts +++ b/packages/workspace/src/atom.ts @@ -1,6 +1,6 @@ import type { WorkspaceAdapter } from '@affine/env/workspace'; import { WorkspaceFlavour, WorkspaceVersion } from '@affine/env/workspace'; -import type { EditorContainer } from '@blocksuite/editor'; +import type { BlockHub } from '@blocksuite/blocks'; import { assertExists } from '@blocksuite/global/utils'; import { atom } from 'jotai'; import Router from 'next/router'; @@ -264,8 +264,7 @@ rootCurrentPageIdAtom.onMount = set => { return; }; -// current editor atom, each app should have only one editor in the same time -export const rootCurrentEditorAtom = atom | null>( - null -); +// blocksuite atoms, +// each app should have only one block-hub in the same time +export const rootBlockHubAtom = atom | null>(null); //#endregion