feat(core): support block or element reference links (#7946)

Upstreams: https://github.com/toeverything/blocksuite/pull/8021

* open doc with mode
* monitor mode changes in query string
* scroll anchoring

https://github.com/user-attachments/assets/681abff8-e51b-47ea-bb71-447e8b312142

https://github.com/user-attachments/assets/e73ed4c0-4e33-45f8-9db4-d8eed3525d05
This commit is contained in:
fundon
2024-09-03 09:49:14 +00:00
parent e7b53641d7
commit 98d9295259
6 changed files with 37 additions and 37 deletions

View File

@@ -29,23 +29,23 @@ import * as styles from './styles.css';
import { scrollAnchoring } from './utils'; import { scrollAnchoring } from './utils';
export interface PageReferenceRendererOptions { export interface PageReferenceRendererOptions {
docMode: DocMode | null;
pageId: string; pageId: string;
docCollection: DocCollection; docCollection: DocCollection;
pageMetaHelper: ReturnType<typeof useDocMetaHelper>; pageMetaHelper: ReturnType<typeof useDocMetaHelper>;
journalHelper: ReturnType<typeof useJournalHelper>; journalHelper: ReturnType<typeof useJournalHelper>;
t: ReturnType<typeof useI18n>; t: ReturnType<typeof useI18n>;
docMode?: DocMode;
// linking doc with block or element // linking doc with block or element
blockIds?: string[]; blockIds?: string[];
elementIds?: string[]; elementIds?: string[];
} }
// use a function to be rendered in the lit renderer // use a function to be rendered in the lit renderer
export function pageReferenceRenderer({ export function pageReferenceRenderer({
docMode,
pageId, pageId,
pageMetaHelper, pageMetaHelper,
journalHelper, journalHelper,
t, t,
docMode,
blockIds, blockIds,
elementIds, elementIds,
}: PageReferenceRendererOptions) { }: PageReferenceRendererOptions) {
@@ -93,8 +93,8 @@ export function AffinePageReference({
isSameDoc = false, isSameDoc = false,
std, std,
}: { }: {
docCollection: DocCollection;
pageId: string; pageId: string;
docCollection: DocCollection;
wrapper?: React.ComponentType<PropsWithChildren>; wrapper?: React.ComponentType<PropsWithChildren>;
mode?: DocMode; mode?: DocMode;
params?: { params?: {

View File

@@ -8,7 +8,7 @@ import { EditorService } from '@affine/core/modules/editor';
import { I18n } from '@affine/i18n'; import { I18n } from '@affine/i18n';
import type { DatabaseBlockModel, MenuOptions } from '@blocksuite/blocks'; import type { DatabaseBlockModel, MenuOptions } from '@blocksuite/blocks';
import { LinkIcon } from '@blocksuite/icons/lit'; import { LinkIcon } from '@blocksuite/icons/lit';
import { type FrameworkProvider } from '@toeverything/infra'; import type { FrameworkProvider } from '@toeverything/infra';
import type { TemplateResult } from 'lit'; import type { TemplateResult } from 'lit';
export function createDatabaseOptionsConfig(framework: FrameworkProvider) { export function createDatabaseOptionsConfig(framework: FrameworkProvider) {
@@ -54,12 +54,12 @@ function createCopyLinkToBlockMenuItem(
const baseUrl = getAffineCloudBaseUrl(); const baseUrl = getAffineCloudBaseUrl();
if (!baseUrl) return; if (!baseUrl) return;
const pageId = model.doc.id;
const { editor } = framework.get(EditorService); const { editor } = framework.get(EditorService);
const mode = editor.mode$.value; const mode = editor.mode$.value;
if (mode === 'edgeless') return; if (mode === 'edgeless') return;
const pageId = editor.doc.id;
const workspaceId = editor.doc.workspace.id; const workspaceId = editor.doc.workspace.id;
const options: UseSharingUrl = { const options: UseSharingUrl = {
workspaceId, workspaceId,

View File

@@ -52,7 +52,6 @@ export const resolveRouteLinkMeta = (href: string) => {
workspaceId, workspaceId,
moduleName: 'doc' as const, moduleName: 'doc' as const,
docId: moduleName, docId: moduleName,
blockId: hash.slice(1),
}; };
} }
} }

View File

@@ -1,7 +1,7 @@
import type { BlockComponent, EditorHost } from '@blocksuite/block-std'; import type { BlockComponent, EditorHost } from '@blocksuite/block-std';
import { import {
AffineReference, AffineReference,
type DocMode, DocMode,
type EmbedLinkedDocModel, type EmbedLinkedDocModel,
type EmbedSyncedDocModel, type EmbedSyncedDocModel,
type ImageBlockModel, type ImageBlockModel,
@@ -135,7 +135,7 @@ function resolvePeekInfoFromPeekTarget(
return { return {
type: 'doc', type: 'doc',
docId, docId,
mode: 'edgeless' as DocMode, mode: DocMode.Edgeless,
xywh: refModel.xywh, xywh: refModel.xywh,
}; };
} }

View File

@@ -112,8 +112,7 @@ export const DocPeekViewControls = ({
nameKey: 'open', nameKey: 'open',
onClick: () => { onClick: () => {
// TODO(@Peng): for frame blocks, we should mimic "view in edgeless" button behavior // TODO(@Peng): for frame blocks, we should mimic "view in edgeless" button behavior
workbench.openDoc({ docId, blockId, mode }); workbench.openDoc({ docId, mode, blockId });
peekView.close('none'); peekView.close('none');
}, },
}, },
@@ -122,7 +121,7 @@ export const DocPeekViewControls = ({
nameKey: 'new-tab', nameKey: 'new-tab',
name: t['com.affine.peek-view-controls.open-doc-in-new-tab'](), name: t['com.affine.peek-view-controls.open-doc-in-new-tab'](),
onClick: () => { onClick: () => {
workbench.openDoc(docId, { at: 'new-tab' }); workbench.openDoc({ docId, mode }, { at: 'new-tab' });
peekView.close('none'); peekView.close('none');
}, },
}, },
@@ -131,7 +130,7 @@ export const DocPeekViewControls = ({
nameKey: 'split-view', nameKey: 'split-view',
name: t['com.affine.peek-view-controls.open-doc-in-split-view'](), name: t['com.affine.peek-view-controls.open-doc-in-split-view'](),
onClick: () => { onClick: () => {
workbench.openDoc(docId, { at: 'beside' }); workbench.openDoc({ docId, mode }, { at: 'beside' });
peekView.close('none'); peekView.close('none');
}, },
}, },

View File

@@ -28,8 +28,7 @@ const useLoadDoc = (pageId: string) => {
const docRecord = useLiveData(docRecordList.doc$(pageId)); const docRecord = useLiveData(docRecordList.doc$(pageId));
const viewService = useService(ViewService); const viewService = useService(ViewService);
const queryString = useLiveData( const queryString$ = viewService.view.queryString$<{
viewService.view.queryString$<{
mode?: string; mode?: string;
blockIds?: string[]; blockIds?: string[];
elementIds?: string[]; elementIds?: string[];
@@ -41,20 +40,22 @@ const useLoadDoc = (pageId: string) => {
blockIds: value => (value.length ? value.split(',') : []), blockIds: value => (value.length ? value.split(',') : []),
elementIds: value => (value.length ? value.split(',') : []), elementIds: value => (value.length ? value.split(',') : []),
}, },
});
const modeInQuery = useLiveData(
queryString$.map(q => {
if (q.mode && DocModes.includes(q.mode)) {
return q.mode as DocMode;
}
return null;
}) })
); );
const queryStringMode =
queryString.mode && DocModes.includes(queryString.mode)
? (queryString.mode as DocMode)
: null;
// We only read the querystring mode when entering, so use useState here. // We only read the querystring mode when entering, so use useState here.
const [initialQueryStringMode] = useState(() => queryStringMode); const [initialQueryStringSelector] = useState(() => {
const [initialQueryStringSelector] = useState(() => ({ const { blockIds, elementIds } = queryString$.value;
blockIds: queryString.blockIds, return { blockIds, elementIds };
elementIds: queryString.elementIds, });
}));
const [doc, setDoc] = useState<Doc | null>(null); const [doc, setDoc] = useState<Doc | null>(null);
const [editor, setEditor] = useState<Editor | null>(null); const [editor, setEditor] = useState<Editor | null>(null);
@@ -75,17 +76,18 @@ const useLoadDoc = (pageId: string) => {
if (!doc) { if (!doc) {
return; return;
} }
const editor = doc.scope const editor = doc.scope
.get(EditorsService) .get(EditorsService)
.createEditor( .createEditor(
initialQueryStringMode || doc.getPrimaryMode() || ('page' as DocMode), modeInQuery || doc.getPrimaryMode() || ('page' as DocMode),
initialQueryStringSelector initialQueryStringSelector
); );
setEditor(editor); setEditor(editor);
return () => { return () => {
editor.dispose(); editor.dispose();
}; };
}, [doc, initialQueryStringMode, initialQueryStringSelector]); }, [doc, modeInQuery, initialQueryStringSelector]);
// update editor mode to queryString // update editor mode to queryString
useEffect(() => { useEffect(() => {