refactor(core): better block link (#8082)

This commit is contained in:
EYHN
2024-09-04 08:13:21 +00:00
parent 2799e8cd43
commit 01b2339173
17 changed files with 282 additions and 253 deletions
@@ -6,7 +6,6 @@ import {
} from '@affine/core/modules/peek-view';
import { WorkbenchLink } from '@affine/core/modules/workbench';
import { useI18n } from '@affine/i18n';
import type { BlockStdScope } from '@blocksuite/block-std';
import type { DocMode } from '@blocksuite/blocks';
import {
BlockLinkIcon,
@@ -17,16 +16,16 @@ import {
} from '@blocksuite/icons/rc';
import type { DocCollection } from '@blocksuite/store';
import { useService } from '@toeverything/infra';
import { nanoid } from 'nanoid';
import {
type PropsWithChildren,
useCallback,
useEffect,
useMemo,
useRef,
useState,
} from 'react';
import * as styles from './styles.css';
import { scrollAnchoring } from './utils';
export interface PageReferenceRendererOptions {
pageId: string;
@@ -90,8 +89,6 @@ export function AffinePageReference({
wrapper: Wrapper,
mode = 'page',
params = {},
isSameDoc = false,
std,
}: {
pageId: string;
docCollection: DocCollection;
@@ -102,16 +99,10 @@ export function AffinePageReference({
blockIds?: string[];
elementIds?: string[];
};
isSameDoc?: boolean;
std?: BlockStdScope;
}) {
const pageMetaHelper = useDocMetaHelper(docCollection);
const journalHelper = useJournalHelper(docCollection);
const t = useI18n();
const [anchor, setAnchor] = useState<{
mode: DocMode;
id: string;
} | null>(null);
const { mode: linkedWithMode, blockIds, elementIds } = params;
@@ -128,21 +119,11 @@ export function AffinePageReference({
const ref = useRef<HTMLAnchorElement>(null);
const [refreshKey, setRefreshKey] = useState<string>(() => nanoid());
const peekView = useService(PeekViewService).peekView;
const isInPeekView = useInsidePeekView();
useEffect(() => {
if (isSameDoc) {
if (mode === 'edgeless' && elementIds?.length) {
setAnchor({ mode, id: elementIds[0] });
} else if (blockIds?.length) {
setAnchor({ mode, id: blockIds[0] });
}
} else {
setAnchor(null);
}
}, [std, isSameDoc, mode, blockIds, elementIds]);
const onClick = useCallback(
(e: React.MouseEvent) => {
if (e.shiftKey && ref.current) {
@@ -151,33 +132,34 @@ export function AffinePageReference({
peekView.open(ref.current).catch(console.error);
}
if (std && anchor) {
e.preventDefault();
e.stopPropagation();
const { mode, id } = anchor;
scrollAnchoring(std, mode, id);
} else if (isInPeekView) {
if (isInPeekView) {
peekView.close();
}
// update refresh key
setRefreshKey(nanoid());
return;
},
[isInPeekView, peekView, anchor, std]
[isInPeekView, peekView]
);
// A block/element reference link
const search = new URLSearchParams();
if (linkedWithMode) {
search.set('mode', linkedWithMode);
}
if (blockIds?.length) {
search.set('blockIds', blockIds.join(','));
}
if (elementIds?.length) {
search.set('elementIds', elementIds.join(','));
}
const query = useMemo(() => {
// A block/element reference link
const search = new URLSearchParams();
if (linkedWithMode) {
search.set('mode', linkedWithMode);
}
if (blockIds?.length) {
search.set('blockIds', blockIds.join(','));
}
if (elementIds?.length) {
search.set('elementIds', elementIds.join(','));
}
search.set('refreshKey', refreshKey);
const query = search.size > 0 ? `?${search.toString()}` : '';
return search.size > 0 ? `?${search.toString()}` : '';
}, [blockIds, elementIds, linkedWithMode, refreshKey]);
return (
<WorkbenchLink
@@ -1,7 +1,6 @@
import type { EditorSelector } from '@affine/core/modules/editor';
import { EditorService } from '@affine/core/modules/editor';
import type { ReferenceInfo } from '@blocksuite/affine-model';
import type { DocMode } from '@blocksuite/blocks';
import type { InlineEditor } from '@blocksuite/inline/inline-editor';
import type {
AffineEditorContainer,
DocTitle,
@@ -9,6 +8,7 @@ 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 {
@@ -18,10 +18,8 @@ import {
useLayoutEffect,
useMemo,
useRef,
useState,
} from 'react';
import { scrollAnchoring } from '../../affine/reference-link/utils';
import { BlocksuiteDocEditor, BlocksuiteEdgelessEditor } from './lit-adaper';
import * as styles from './styles.css';
@@ -46,7 +44,6 @@ interface BlocksuiteEditorContainerProps {
shared?: boolean;
className?: string;
style?: React.CSSProperties;
defaultEditorSelector?: EditorSelector;
}
// mimic the interface of the webcomponent and expose slots & host
@@ -60,21 +57,14 @@ export const BlocksuiteEditorContainer = forwardRef<
AffineEditorContainer,
BlocksuiteEditorContainerProps
>(function AffineEditorContainer(
{ page, mode, className, style, shared, defaultEditorSelector },
{ page, mode, className, style, shared },
ref
) {
const editorService = useService(EditorService);
const rootRef = useRef<HTMLDivElement>(null);
const docRef = useRef<PageEditor>(null);
const docTitleRef = useRef<DocTitle>(null);
const edgelessRef = useRef<EdgelessEditor>(null);
const [anchor] = useState<string | null>(() => {
if (mode === 'edgeless' && defaultEditorSelector?.elementIds?.length) {
return defaultEditorSelector.elementIds[0];
} else if (defaultEditorSelector?.blockIds?.length) {
return defaultEditorSelector.blockIds[0];
}
return null;
});
const slots: BlocksuiteEditorContainerRef['slots'] = useMemo(() => {
return {
@@ -180,40 +170,24 @@ export const BlocksuiteEditorContainer = forwardRef<
}, [affineEditorContainerProxy, ref]);
useEffect(() => {
if (anchor) {
let canceled = false;
affineEditorContainerProxy.updateComplete
.then(() => {
if (!canceled) {
const std = affineEditorContainerProxy.host?.std;
if (std) {
scrollAnchoring(std, mode, anchor);
}
}
})
.catch(console.error);
return () => {
canceled = true;
};
} else {
// if no anchor, focus the title
let canceled = false;
let canceled = false;
let unsubscribe: () => void = () => {};
affineEditorContainerProxy.updateComplete
.then(() => {
if (!canceled) {
const title = docTitleRef.current?.querySelector<
HTMLElement & { inlineEditor: InlineEditor }
>('rich-text');
title?.inlineEditor.focusEnd();
}
})
.catch(console.error);
return () => {
canceled = true;
};
}
}, [anchor, affineEditorContainerProxy, mode]);
affineEditorContainerProxy.updateComplete
.then(() => {
if (!canceled) {
unsubscribe = editorService.editor.bindEditorContainer(
affineEditorContainerProxy,
docTitleRef.current as DocTitle
);
}
})
.catch(console.error);
return () => {
canceled = true;
unsubscribe();
};
}, [affineEditorContainerProxy, mode, editorService]);
const handleClickPageModeBlank = useCallback(() => {
affineEditorContainerProxy.host?.std.command.exec(
@@ -1,5 +1,4 @@
import { EditorLoading } from '@affine/component/page-detail-skeleton';
import type { EditorSelector } from '@affine/core/modules/editor';
import type { DocMode } from '@blocksuite/blocks';
import { assertExists } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '@blocksuite/presets';
@@ -30,7 +29,6 @@ export type EditorProps = {
onLoadEditor?: (editor: AffineEditorContainer) => () => void;
style?: CSSProperties;
className?: string;
defaultEditorSelector?: EditorSelector;
};
function usePageRoot(page: Doc) {
@@ -57,15 +55,7 @@ function usePageRoot(page: Doc) {
const BlockSuiteEditorImpl = forwardRef<AffineEditorContainer, EditorProps>(
function BlockSuiteEditorImpl(
{
mode,
page,
className,
onLoadEditor,
shared,
style,
defaultEditorSelector,
},
{ mode, page, className, onLoadEditor, shared, style },
ref
) {
usePageRoot(page);
@@ -115,7 +105,6 @@ const BlockSuiteEditorImpl = forwardRef<AffineEditorContainer, EditorProps>(
ref={onRefChange}
className={className}
style={style}
defaultEditorSelector={defaultEditorSelector}
/>
);
}
@@ -77,20 +77,16 @@ const usePatchSpecs = (page: Doc, shared: boolean, mode: DocMode) => {
const pageId = data.pageId;
if (!pageId) return <span />;
const isSameDoc = pageId === page.id;
return (
<AffinePageReference
docCollection={page.collection}
pageId={pageId}
mode={mode}
params={data.params}
std={reference.std}
isSameDoc={isSameDoc}
/>
);
};
}, [mode, page.collection, page.id]);
}, [mode, page.collection]);
const specs = useMemo(() => {
return mode === 'edgeless'
@@ -40,6 +40,7 @@ export const EditorModeSwitch = () => {
const togglePage = useCallback(() => {
if (currentMode === 'page' || isSharedMode || trash) return;
editor.setMode('page');
editor.setSelector(undefined);
editor.doc.setPrimaryMode('page');
toast(t['com.affine.toastMessage.pageMode']());
track.$.header.actions.switchPageMode({ mode: 'page' });
@@ -48,6 +49,7 @@ export const EditorModeSwitch = () => {
const toggleEdgeless = useCallback(() => {
if (currentMode === 'edgeless' || isSharedMode || trash) return;
editor.setMode('edgeless');
editor.setSelector(undefined);
editor.doc.setPrimaryMode('edgeless');
toast(t['com.affine.toastMessage.edgelessMode']());
track.$.header.actions.switchPageMode({ mode: 'edgeless' });
@@ -11,7 +11,7 @@ import clsx from 'clsx';
import type { CSSProperties } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { type EditorSelector, EditorService } from '../modules/editor';
import { EditorService } from '../modules/editor';
import {
EditorSettingService,
fontStyleOptions,
@@ -35,13 +35,11 @@ export interface PageDetailEditorProps {
docCollection: DocCollection;
pageId: string;
onLoad?: OnLoadEditor;
defaultEditorSelector?: EditorSelector;
}
const PageDetailEditorMain = memo(function PageDetailEditorMain({
page,
onLoad,
defaultEditorSelector,
}: PageDetailEditorProps & { page: BlockSuiteDoc }) {
const editor = useService(EditorService).editor;
const mode = useLiveData(editor.mode$);
@@ -108,7 +106,6 @@ const PageDetailEditorMain = memo(function PageDetailEditorMain({
mode={mode}
page={page}
shared={isSharedMode}
defaultEditorSelector={defaultEditorSelector}
onLoadEditor={onLoadEditor}
/>
);
@@ -1,29 +1,32 @@
import type { DocMode } from '@blocksuite/blocks';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { InlineEditor } from '@blocksuite/inline/inline-editor';
import type { AffineEditorContainer, DocTitle } from '@blocksuite/presets';
import type { DocService, WorkspaceService } from '@toeverything/infra';
import { Entity, LiveData } from '@toeverything/infra';
import { isEqual } from 'lodash-es';
import type { WorkbenchView } from '../../workbench';
import { EditorScope } from '../scopes/editor';
import type { EditorSelector } from '../types';
import { scrollAnchoring } from '../utils/scroll-anchoring';
export class Editor extends Entity<{
defaultMode: DocMode;
defaultEditorSelector?: EditorSelector;
}> {
export class Editor extends Entity {
readonly scope = this.framework.createScope(EditorScope, {
editor: this as Editor,
});
readonly mode$ = new LiveData(this.props.defaultMode);
readonly selector$ = new LiveData<EditorSelector | undefined>(
this.props.defaultEditorSelector
);
readonly mode$ = new LiveData<DocMode>('page');
readonly selector$ = new LiveData<EditorSelector | undefined>(undefined);
readonly doc = this.docService.doc;
readonly isSharedMode =
this.workspaceService.workspace.openOptions.isSharedMode;
readonly editorContainer$ = new LiveData<AffineEditorContainer | null>(null);
setSelector(selector: EditorSelector | undefined) {
this.selector$.next(selector);
}
toggleMode() {
this.mode$.next(this.mode$.value === 'edgeless' ? 'page' : 'edgeless');
}
@@ -36,6 +39,149 @@ export class Editor extends Entity<{
this.editorContainer$.next(editorContainer);
}
/**
* sync editor params with view query string
*/
bindWorkbenchView(view: WorkbenchView) {
// eslint-disable-next-line rxjs/finnish
const viewParams$ = view.queryString$<{
mode?: DocMode;
blockIds?: string[];
elementIds?: string[];
refreshKey?: string;
}>({
// Cannot handle single id situation correctly: `blockIds=xxx`
arrayFormat: 'none',
types: {
mode: value =>
value === 'page' || value === 'edgeless' ? value : undefined,
blockIds: value => (value.length ? value.split(',') : []),
elementIds: value => (value.length ? value.split(',') : []),
refreshKey: 'string',
},
});
const stablePrimaryMode = this.doc.getPrimaryMode();
const editorParams$ = LiveData.computed(get => {
const selector = get(this.selector$);
return {
mode: get(this.mode$),
blockIds: selector?.blockIds,
elementIds: selector?.elementIds,
refreshKey: selector?.refreshKey,
};
});
// prevent infinite loop
let updating = false;
const unsubscribeViewParams = viewParams$.subscribe(params => {
if (updating) return;
updating = true;
// when view params changed, sync to editor
try {
const mode =
viewParams$.value.mode || stablePrimaryMode || ('page' as DocMode);
if (mode !== editorParams$.value.mode) {
this.setMode(mode);
}
const newSelector = {
blockIds: params.blockIds,
elementIds: params.elementIds,
refreshKey: params.refreshKey,
};
if (!isEqual(newSelector, editorParams$.value)) {
this.setSelector({
blockIds: params.blockIds,
elementIds: params.elementIds,
refreshKey: params.refreshKey,
});
}
} finally {
updating = false;
}
});
const unsubscribeEditorParams = editorParams$.subscribe(params => {
if (updating) return;
updating = true;
try {
// when editor params changed, sync to view
const newQueryString: any = {};
let updated = false;
if (params.mode !== viewParams$.value.mode) {
newQueryString.mode = params.mode;
updated = true;
}
const stringBlockIds = params.blockIds?.join(',');
const stringElementIds = params.elementIds?.join(',');
const stringViewBlockIds = viewParams$.value.blockIds?.join(',');
const stringViewElementIds = viewParams$.value.elementIds?.join(',');
if (
stringBlockIds !== stringViewBlockIds ||
stringElementIds !== stringViewElementIds ||
params.refreshKey !== viewParams$.value.refreshKey
) {
newQueryString.blockIds = stringBlockIds;
newQueryString.elementIds = stringElementIds;
newQueryString.refreshKey = params.refreshKey;
updated = true;
}
if (updated) {
view.updateQueryString(newQueryString, { replace: true });
}
} finally {
updating = false;
}
});
return () => {
unsubscribeEditorParams.unsubscribe();
unsubscribeViewParams.unsubscribe();
};
}
bindEditorContainer(
editorContainer: AffineEditorContainer,
docTitle: DocTitle
) {
const focusAt$ = LiveData.computed(get => {
const selector = get(this.selector$);
const id =
get(this.mode$) === 'edgeless' && selector?.elementIds?.length
? selector?.elementIds?.[0]
: selector?.blockIds?.[0];
if (id) {
return { id, refreshKey: selector?.refreshKey };
} else {
return null;
}
});
if (focusAt$.value === null) {
const title = docTitle.querySelector<
HTMLElement & { inlineEditor: InlineEditor }
>('rich-text');
title?.inlineEditor.focusEnd();
}
const unsubscribe = focusAt$
.distinctUntilChanged(
(a, b) => a?.id === b?.id && a?.refreshKey === b?.refreshKey
)
.subscribe(params => {
if (params?.id) {
const std = editorContainer.host?.std;
if (std) {
scrollAnchoring(std, this.mode$.value, params.id);
}
}
});
return () => {
unsubscribe.unsubscribe();
};
}
constructor(
private readonly docService: DocService,
private readonly workspaceService: WorkspaceService
@@ -1,14 +1,9 @@
import type { DocMode } from '@blocksuite/blocks';
import { Service } from '@toeverything/infra';
import { Editor } from '../entities/editor';
import type { EditorSelector } from '../types';
export class EditorsService extends Service {
createEditor(defaultMode: DocMode, defaultEditorSelector?: EditorSelector) {
return this.framework.createEntity(Editor, {
defaultMode,
defaultEditorSelector,
});
createEditor() {
return this.framework.createEntity(Editor);
}
}
@@ -1,4 +1,5 @@
export type EditorSelector = {
blockIds?: string[];
elementIds?: string[];
refreshKey?: string;
};
@@ -39,6 +39,7 @@ function scrollAnchoringInEdgelessMode(
h / service.viewport.zoom
);
} else {
if (!service.getElementById) return;
const model = service.getElementById(id);
if (!model) return;
@@ -5,16 +5,17 @@ import { AffineErrorBoundary } from '@affine/core/components/affine/affine-error
import { BlockSuiteEditor } from '@affine/core/components/blocksuite/block-suite-editor';
import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer';
import { useNavigateHelper } from '@affine/core/hooks/use-navigate-helper';
import { EditorService } from '@affine/core/modules/editor';
import { PageNotFound } from '@affine/core/pages/404';
import { DebugLogger } from '@affine/debug';
import type { DocMode, EdgelessRootService } from '@blocksuite/blocks';
import { Bound, DisposableGroup } from '@blocksuite/global/utils';
import type { AffineEditorContainer } from '@blocksuite/presets';
import {
DocsService,
FrameworkScope,
useLiveData,
useService,
useServices,
} from '@toeverything/infra';
import clsx from 'clsx';
import { useCallback, useEffect, useState } from 'react';
@@ -62,27 +63,21 @@ function fitViewport(
}
}
export function DocPeekPreview({
docId,
blockIds,
elementIds,
mode,
function DocPeekPreviewEditor({
xywh,
}: {
docId: string;
blockIds?: string[];
elementIds?: string[];
mode?: DocMode;
xywh?: `[${number},${number},${number},${number}]`;
}) {
const { doc, workspace, editor, loading } = useEditor(docId, mode, {
blockIds,
elementIds,
const { editorService } = useServices({
EditorService,
});
const editor = editorService.editor;
const doc = editor.doc;
const workspace = editor.doc.workspace;
const mode = useLiveData(editor.mode$);
const { jumpToTag } = useNavigateHelper();
const workbench = useService(WorkbenchService).workbench;
const peekView = useService(PeekViewService).peekView;
const defaultEditorSelector = useLiveData(editor?.selector$);
const [editorElement, setEditorElement] =
useState<AffineEditorContainer | null>(null);
@@ -90,27 +85,16 @@ export function DocPeekPreview({
setEditorElement(editor);
};
const docs = useService(DocsService);
const [resolvedMode, setResolvedMode] = useState<DocMode | undefined>(mode);
useEffect(() => {
editorElement?.updateComplete
.then(() => {
if (resolvedMode === 'edgeless') {
if (mode === 'edgeless') {
fitViewport(editorElement, xywh);
}
})
.catch(console.error);
return;
}, [editorElement, resolvedMode, xywh]);
useEffect(() => {
if (!mode || !resolvedMode) {
setResolvedMode(
docs.list.doc$(docId).value?.primaryMode$.value || 'page'
);
}
}, [docId, docs.list, resolvedMode, mode]);
}, [editorElement, mode, xywh]);
useEffect(() => {
const disposable = AIProvider.slots.requestOpenWithChat.on(() => {
@@ -164,20 +148,11 @@ export function DocPeekPreview({
}, [editorElement, jumpToTag, peekView, workspace.id]);
const openOutlinePanel = useCallback(() => {
workbench.openDoc(docId);
workbench.openDoc(doc.id);
workbench.openSidebar();
workbench.activeView$.value.activeSidebarTab('outline');
peekView.close();
}, [docId, peekView, workbench]);
// if sync engine has been synced and the page is null, show 404 page.
if (!doc || !resolvedMode || !editor) {
return loading || !resolvedMode ? (
<PageDetailSkeleton key="current-page-is-null" />
) : (
<PageNotFound noPermission />
);
}
}, [doc, peekView, workbench]);
return (
<AffineErrorBoundary>
@@ -185,20 +160,15 @@ export function DocPeekPreview({
<Scrollable.Viewport
className={clsx('affine-page-viewport', styles.affineDocViewport)}
>
<FrameworkScope scope={doc.scope}>
<FrameworkScope scope={editor.scope}>
<BlockSuiteEditor
ref={onRef}
className={styles.editor}
mode={resolvedMode}
defaultEditorSelector={defaultEditorSelector}
page={doc.blockSuiteDoc}
/>
</FrameworkScope>
</FrameworkScope>
<BlockSuiteEditor
ref={onRef}
className={styles.editor}
mode={mode}
page={doc.blockSuiteDoc}
/>
<EditorOutlineViewer
editor={editorElement}
show={resolvedMode === 'page'}
show={mode === 'page'}
openOutlinePanel={openOutlinePanel}
/>
</Scrollable.Viewport>
@@ -208,3 +178,39 @@ export function DocPeekPreview({
</AffineErrorBoundary>
);
}
export function DocPeekPreview({
docId,
blockIds,
elementIds,
mode,
xywh,
}: {
docId: string;
blockIds?: string[];
elementIds?: string[];
mode?: DocMode;
xywh?: `[${number},${number},${number},${number}]`;
}) {
const { doc, editor, loading } = useEditor(docId, mode, {
blockIds,
elementIds,
});
// if sync engine has been synced and the page is null, show 404 page.
if (!doc || !editor) {
return loading ? (
<PageDetailSkeleton key="current-page-is-null" />
) : (
<PageNotFound noPermission />
);
}
return (
<FrameworkScope scope={doc.scope}>
<FrameworkScope scope={editor.scope}>
<DocPeekPreviewEditor xywh={xywh} />
</FrameworkScope>
</FrameworkScope>
);
}
@@ -41,12 +41,9 @@ export const useEditor = (
if (!doc) {
return;
}
const editor = doc.scope
.get(EditorsService)
.createEditor(
preferModeRef.current || doc.primaryMode$.value,
preferSelectorRef.current
);
const editor = doc.scope.get(EditorsService).createEditor();
editor.setMode(preferModeRef.current || doc.primaryMode$.value);
editor.setSelector(preferSelectorRef.current);
setEditor(editor);
return () => {
editor.dispose();
@@ -86,9 +86,9 @@ export class View extends Entity<{
parseBooleans: true,
}
) {
return this.location$.map(
location => queryString.parse(location.search, options) as Partial<T>
);
return this.location$
.selector(v => v.search)
.map(search => queryString.parse(search, options) as Partial<T>);
}
updateQueryString<T extends Record<string, unknown>>(
@@ -1,3 +1,4 @@
export { View as WorkbenchView } from './entities/view';
export { Workbench } from './entities/workbench';
export { ViewScope } from './scopes/view';
export { WorkbenchService } from './services/workbench';
@@ -1,7 +1,6 @@
import type { Editor } from '@affine/core/modules/editor';
import { EditorsService } from '@affine/core/modules/editor';
import { ViewService } from '@affine/core/modules/workbench/services/view';
import { type DocMode, DocModes } from '@blocksuite/blocks';
import type { Doc } from '@toeverything/infra';
import {
DocsService,
@@ -26,80 +25,24 @@ const useLoadDoc = (pageId: string) => {
const docRecord = useLiveData(docRecordList.doc$(pageId));
const viewService = useService(ViewService);
const queryString$ = viewService.view.queryString$<{
mode?: string;
blockIds?: string[];
elementIds?: string[];
}>({
// Cannot handle single id situation correctly: `blockIds=xxx`
arrayFormat: 'none',
types: {
mode: 'string',
blockIds: value => (value.length ? value.split(',') : []),
elementIds: value => (value.length ? value.split(',') : []),
},
});
const modeInQuery = useLiveData(
queryString$.map(q => {
if (q.mode && DocModes.includes(q.mode as DocMode)) {
return q.mode as DocMode;
}
return null;
})
);
// We only read the querystring mode when entering, so use useState here.
const [initialQueryStringSelector] = useState(() => {
const { blockIds, elementIds } = queryString$.value;
return { blockIds, elementIds };
});
const [doc, setDoc] = useState<Doc | null>(null);
const [editor, setEditor] = useState<Editor | null>(null);
const editorMode = useLiveData(editor?.mode$);
useLayoutEffect(() => {
if (!docRecord) {
return;
}
const { doc: opened, release } = docsService.open(pageId);
setDoc(opened);
return () => {
release();
};
}, [docRecord, docsService, pageId]);
useLayoutEffect(() => {
if (!doc) {
return;
}
const editor = doc.scope
.get(EditorsService)
.createEditor(
modeInQuery || doc.getPrimaryMode() || ('page' as DocMode),
initialQueryStringSelector
);
const { doc, release } = docsService.open(pageId);
setDoc(doc);
const editor = doc.scope.get(EditorsService).createEditor();
const unbind = editor.bindWorkbenchView(viewService.view);
setEditor(editor);
return () => {
unbind();
editor.dispose();
release();
};
}, [doc, modeInQuery, initialQueryStringSelector]);
// update editor mode to queryString
useEffect(() => {
if (editorMode) {
viewService.view.updateQueryString(
{
mode: editorMode,
},
{
replace: true,
}
);
}
}, [editorMode, viewService.view]);
}, [docRecord, docsService, pageId, viewService.view]);
// set sync engine priority target
useEffect(() => {
@@ -33,7 +33,7 @@ import {
WorkspaceService,
} from '@toeverything/infra';
import clsx from 'clsx';
import { memo, useCallback, useEffect, useRef, useState } from 'react';
import { memo, useCallback, useEffect, useRef } from 'react';
import { useParams } from 'react-router-dom';
import type { Map as YMap } from 'yjs';
@@ -93,7 +93,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
const isInTrash = useLiveData(doc.meta$.map(meta => meta.trash));
const { openPage, jumpToPageBlock, jumpToTag } = useNavigateHelper();
const editorContainer = useLiveData(editor.editorContainer$);
const [defaultSelector] = useState(() => editor.selector$.value);
const isSideBarOpen = useLiveData(workbench.sidebarOpen$);
const { appSettings } = useAppSettingHelper();
@@ -290,7 +289,6 @@ const DetailPageImpl = memo(function DetailPageImpl() {
pageId={doc.id}
onLoad={onLoad}
docCollection={docCollection}
defaultEditorSelector={defaultSelector}
/>
</Scrollable.Viewport>
<Scrollable.Scrollbar
@@ -159,7 +159,8 @@ const SharePageInner = ({
setPage(doc);
const editor = doc.scope.get(EditorsService).createEditor(publishMode);
const editor = doc.scope.get(EditorsService).createEditor();
editor.setMode(publishMode);
setEditor(editor);
})