mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat(core): auto select block when jump to block (#4858)
Co-authored-by: Peng Xiao <pengxiao@outlook.com>
This commit is contained in:
@@ -3,7 +3,6 @@ import type {
|
||||
WorkspaceFlavour,
|
||||
WorkspaceUISchema,
|
||||
} from '@affine/env/workspace';
|
||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
||||
import { lazy, useCallback } from 'react';
|
||||
|
||||
import type { OnLoadEditor } from '../../components/page-detail-editor';
|
||||
@@ -50,7 +49,6 @@ export const UI = {
|
||||
return (
|
||||
<PageDetailEditor
|
||||
pageId={currentPageId}
|
||||
onInit={useCallback(async page => initEmptyPage(page), [])}
|
||||
onLoad={onLoad}
|
||||
workspace={workspace.blockSuiteWorkspace}
|
||||
/>
|
||||
|
||||
@@ -25,7 +25,6 @@ import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
||||
import { buildShowcaseWorkspace } from '@toeverything/infra/blocksuite';
|
||||
import { useAtomValue } from 'jotai';
|
||||
import { nanoid } from 'nanoid';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { setPageModeAtom } from '../../atoms';
|
||||
import {
|
||||
@@ -94,7 +93,6 @@ export const LocalAdapter: WorkspaceAdapter<WorkspaceFlavour.LOCAL> = {
|
||||
return (
|
||||
<PageDetailEditor
|
||||
pageId={currentPageId}
|
||||
onInit={useCallback(async page => initEmptyPage(page), [])}
|
||||
onLoad={onLoadEditor}
|
||||
workspace={workspace}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
import { PageNotFoundError } from '@affine/env/constant';
|
||||
import type { WorkspaceFlavour } from '@affine/env/workspace';
|
||||
import { type WorkspaceUISchema } from '@affine/env/workspace';
|
||||
import { initEmptyPage } from '@toeverything/infra/blocksuite';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useWorkspace } from '../../hooks/use-workspace';
|
||||
import { PageDetailEditor, Provider } from '../shared';
|
||||
@@ -18,7 +16,6 @@ export const UI = {
|
||||
return (
|
||||
<PageDetailEditor
|
||||
pageId={currentPageId}
|
||||
onInit={useCallback(async page => initEmptyPage(page), [])}
|
||||
onLoad={onLoadEditor}
|
||||
workspace={workspace.blockSuiteWorkspace}
|
||||
/>
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
import './page-detail-editor.css';
|
||||
|
||||
import { PageNotFoundError } from '@affine/env/constant';
|
||||
import type { LayoutNode } from '@affine/sdk//entry';
|
||||
import type { LayoutNode } from '@affine/sdk/entry';
|
||||
import { rootBlockHubAtom } from '@affine/workspace/atom';
|
||||
import type { BlockHub } from '@blocksuite/blocks';
|
||||
import type { EditorContainer } from '@blocksuite/editor';
|
||||
import { assertExists, DisposableGroup } from '@blocksuite/global/utils';
|
||||
import type { Page, Workspace } from '@blocksuite/store';
|
||||
@@ -40,8 +41,9 @@ import * as styles from './page-detail-editor.css';
|
||||
import { editorContainer, pluginContainer } from './page-detail-editor.css';
|
||||
import { TrashButtonGroup } from './pure/trash-button-group';
|
||||
|
||||
function useRouterHash() {
|
||||
return useLocation().hash.substring(1);
|
||||
declare global {
|
||||
// eslint-disable-next-line no-var
|
||||
var currentEditor: EditorContainer | undefined;
|
||||
}
|
||||
|
||||
export type OnLoadEditor = (page: Page, editor: EditorContainer) => () => void;
|
||||
@@ -50,17 +52,43 @@ export interface PageDetailEditorProps {
|
||||
isPublic?: boolean;
|
||||
workspace: Workspace;
|
||||
pageId: string;
|
||||
onInit: (
|
||||
page: Page,
|
||||
editor: Readonly<EditorContainer>
|
||||
) => Promise<void> | void;
|
||||
onLoad?: OnLoadEditor;
|
||||
}
|
||||
|
||||
function useRouterHash() {
|
||||
return useLocation().hash.substring(1);
|
||||
}
|
||||
|
||||
function useCreateAndSetRootBlockHub(
|
||||
editor?: EditorContainer,
|
||||
showBlockHub?: boolean
|
||||
) {
|
||||
const setBlockHub = useSetAtom(rootBlockHubAtom);
|
||||
useEffect(() => {
|
||||
let canceled = false;
|
||||
let blockHub: BlockHub | undefined;
|
||||
if (editor && showBlockHub) {
|
||||
editor
|
||||
.createBlockHub()
|
||||
.then(bh => {
|
||||
if (canceled) {
|
||||
return;
|
||||
}
|
||||
blockHub = bh;
|
||||
setBlockHub(blockHub);
|
||||
})
|
||||
.catch(console.error);
|
||||
}
|
||||
return () => {
|
||||
canceled = true;
|
||||
blockHub?.remove();
|
||||
};
|
||||
}, [editor, showBlockHub, setBlockHub]);
|
||||
}
|
||||
|
||||
const EditorWrapper = memo(function EditorWrapper({
|
||||
workspace,
|
||||
pageId,
|
||||
onInit,
|
||||
onLoad,
|
||||
isPublic,
|
||||
}: PageDetailEditorProps) {
|
||||
@@ -79,7 +107,6 @@ const EditorWrapper = memo(function EditorWrapper({
|
||||
const pageSetting = useAtomValue(pageSettingAtom);
|
||||
const currentMode = pageSetting?.mode ?? 'page';
|
||||
|
||||
const setBlockHub = useSetAtom(rootBlockHubAtom);
|
||||
const { appSettings } = useAppSettingHelper();
|
||||
|
||||
assertExists(meta);
|
||||
@@ -91,29 +118,6 @@ const EditorWrapper = memo(function EditorWrapper({
|
||||
return fontStyle.value;
|
||||
}, [appSettings.fontStyle]);
|
||||
|
||||
const [loading, setLoading] = useState(true);
|
||||
const blockId = useRouterHash();
|
||||
const blockElement = useMemo(() => {
|
||||
if (!blockId || loading) {
|
||||
return null;
|
||||
}
|
||||
return document.querySelector(`[data-block-id="${blockId}"]`);
|
||||
}, [blockId, loading]);
|
||||
|
||||
useEffect(() => {
|
||||
if (blockElement) {
|
||||
setTimeout(
|
||||
() =>
|
||||
blockElement.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'center',
|
||||
inline: 'center',
|
||||
}),
|
||||
0
|
||||
);
|
||||
}
|
||||
}, [blockElement]);
|
||||
|
||||
const setEditorMode = useCallback(
|
||||
(mode: 'page' | 'edgeless') => {
|
||||
if (mode === 'edgeless') {
|
||||
@@ -125,6 +129,56 @@ const EditorWrapper = memo(function EditorWrapper({
|
||||
[switchToEdgelessMode, switchToPageMode, pageId]
|
||||
);
|
||||
|
||||
const [editor, setEditor] = useState<EditorContainer>();
|
||||
const blockId = useRouterHash();
|
||||
|
||||
useCreateAndSetRootBlockHub(editor, !meta.trash);
|
||||
|
||||
const onLoadEditor = useCallback(
|
||||
(editor: EditorContainer) => {
|
||||
// debug current detail editor
|
||||
globalThis.currentEditor = editor;
|
||||
setEditor(editor);
|
||||
const disposableGroup = new DisposableGroup();
|
||||
disposableGroup.add(
|
||||
page.slots.blockUpdated.once(() => {
|
||||
page.workspace.setPageMeta(page.id, {
|
||||
updatedDate: Date.now(),
|
||||
});
|
||||
})
|
||||
);
|
||||
localStorage.setItem('last_page_id', page.id);
|
||||
if (onLoad) {
|
||||
disposableGroup.add(onLoad(page, editor));
|
||||
}
|
||||
const rootStore = getCurrentStore();
|
||||
const editorItems = rootStore.get(pluginEditorAtom);
|
||||
let disposes: (() => void)[] = [];
|
||||
const renderTimeout = window.setTimeout(() => {
|
||||
disposes = Object.entries(editorItems).map(([id, editorItem]) => {
|
||||
const div = document.createElement('div');
|
||||
div.setAttribute('plugin-id', id);
|
||||
const cleanup = editorItem(div, editor);
|
||||
assertExists(parent);
|
||||
document.body.appendChild(div);
|
||||
return () => {
|
||||
cleanup();
|
||||
document.body.removeChild(div);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
disposableGroup.dispose();
|
||||
clearTimeout(renderTimeout);
|
||||
window.setTimeout(() => {
|
||||
disposes.forEach(dispose => dispose());
|
||||
});
|
||||
};
|
||||
},
|
||||
[onLoad, page]
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Editor
|
||||
@@ -140,55 +194,8 @@ const EditorWrapper = memo(function EditorWrapper({
|
||||
mode={isPublic ? 'page' : currentMode}
|
||||
page={page}
|
||||
onModeChange={setEditorMode}
|
||||
onInit={useCallback(
|
||||
(page: Page, editor: Readonly<EditorContainer>) => {
|
||||
onInit(page, editor);
|
||||
},
|
||||
[onInit]
|
||||
)}
|
||||
setBlockHub={setBlockHub}
|
||||
onLoad={useCallback(
|
||||
(page: Page, editor: EditorContainer) => {
|
||||
const disposableGroup = new DisposableGroup();
|
||||
disposableGroup.add(
|
||||
page.slots.blockUpdated.once(() => {
|
||||
page.workspace.setPageMeta(page.id, {
|
||||
updatedDate: Date.now(),
|
||||
});
|
||||
})
|
||||
);
|
||||
localStorage.setItem('last_page_id', page.id);
|
||||
if (onLoad) {
|
||||
disposableGroup.add(onLoad(page, editor));
|
||||
}
|
||||
const rootStore = getCurrentStore();
|
||||
const editorItems = rootStore.get(pluginEditorAtom);
|
||||
let disposes: (() => void)[] = [];
|
||||
const renderTimeout = window.setTimeout(() => {
|
||||
disposes = Object.entries(editorItems).map(([id, editorItem]) => {
|
||||
const div = document.createElement('div');
|
||||
div.setAttribute('plugin-id', id);
|
||||
const cleanup = editorItem(div, editor);
|
||||
assertExists(parent);
|
||||
document.body.appendChild(div);
|
||||
return () => {
|
||||
cleanup();
|
||||
document.body.removeChild(div);
|
||||
};
|
||||
});
|
||||
});
|
||||
|
||||
return () => {
|
||||
disposableGroup.dispose();
|
||||
clearTimeout(renderTimeout);
|
||||
window.setTimeout(() => {
|
||||
disposes.forEach(dispose => dispose());
|
||||
});
|
||||
setLoading(false);
|
||||
};
|
||||
},
|
||||
[onLoad]
|
||||
)}
|
||||
defaultSelectedBlockId={blockId}
|
||||
onLoadEditor={onLoadEditor}
|
||||
/>
|
||||
{meta.trash && <TrashButtonGroup />}
|
||||
<Bookmark page={page} />
|
||||
|
||||
@@ -2,7 +2,7 @@ import {
|
||||
AppSidebarFallback,
|
||||
appSidebarResizingAtom,
|
||||
} from '@affine/component/app-sidebar';
|
||||
import { BlockHubWrapper } from '@affine/component/block-hub';
|
||||
import { RootBlockHub } from '@affine/component/block-hub';
|
||||
import {
|
||||
type DraggableTitleCellData,
|
||||
PageListDragOverlay,
|
||||
@@ -13,10 +13,7 @@ import {
|
||||
WorkspaceFallback,
|
||||
} from '@affine/component/workspace';
|
||||
import { useAFFiNEI18N } from '@affine/i18n/hooks';
|
||||
import {
|
||||
rootBlockHubAtom,
|
||||
rootWorkspacesMetadataAtom,
|
||||
} from '@affine/workspace/atom';
|
||||
import { rootWorkspacesMetadataAtom } from '@affine/workspace/atom';
|
||||
import { assertExists } from '@blocksuite/global/utils';
|
||||
import type { Page } from '@blocksuite/store';
|
||||
import type { DragEndEvent } from '@dnd-kit/core';
|
||||
@@ -294,7 +291,7 @@ export const WorkspaceLayoutInner = ({
|
||||
>
|
||||
{incompatible ? <WorkspaceUpgrade /> : children}
|
||||
<ToolContainer inTrashPage={inTrashPage}>
|
||||
<BlockHubWrapper blockHubAtom={rootBlockHubAtom} />
|
||||
<RootBlockHub />
|
||||
<HelpIsland showList={pageId ? undefined : showList} />
|
||||
</ToolContainer>
|
||||
</MainContainer>
|
||||
|
||||
@@ -70,7 +70,6 @@ export const Component = (): ReactElement => {
|
||||
isPublic
|
||||
workspace={page.workspace}
|
||||
pageId={page.id}
|
||||
onInit={noop}
|
||||
onLoad={useCallback(() => noop, [])}
|
||||
/>
|
||||
</MainContainer>
|
||||
|
||||
Reference in New Issue
Block a user