From 661594aec82e460b474c29639b92cea4e055be35 Mon Sep 17 00:00:00 2001 From: fundon Date: Fri, 20 Sep 2024 03:42:08 +0000 Subject: [PATCH] feat(core): enhance share page with selector (#8319) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes [BS-1346](https://linear.app/affine-design/issue/BS-1346/白板上的-link-to-block-在只读模式下打开无法跳转和高亮)
🎥 Video uploaded on Graphite:
--- .../specs/custom/spec-patchers.tsx | 12 ++-- .../components/hooks/use-navigate-helper.ts | 8 +-- .../pages/workspace/share/share-page.tsx | 58 ++++++++++++++++++- .../modules/workbench/entities/workbench.ts | 23 +++----- 4 files changed, 71 insertions(+), 30 deletions(-) diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx index 3a136d288b..7d64fe24a4 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/specs/custom/spec-patchers.tsx @@ -377,7 +377,6 @@ export function patchQuickSearchService(framework: FrameworkProvider) { (item.name === 'Linked Doc' || item.name === 'Link') ) { item.action = async ({ rootComponent }) => { - // TODO(@Mirone): fix the type // @ts-expect-error fixme const { success, insertedLinkType } = // @ts-expect-error fixme @@ -385,7 +384,6 @@ export function patchQuickSearchService(framework: FrameworkProvider) { if (!success) return; - // TODO(@Mirone): fix the type insertedLinkType ?.then( (type: { @@ -394,17 +392,17 @@ export function patchQuickSearchService(framework: FrameworkProvider) { const flavour = type?.flavour; if (!flavour) return; + if (flavour === 'affine:bookmark') { + track.doc.editor.slashMenu.bookmark(); + return; + } + if (flavour === 'affine:embed-linked-doc') { track.doc.editor.slashMenu.linkDoc({ control: 'linkDoc', }); return; } - - if (flavour === 'affine:bookmark') { - track.doc.editor.slashMenu.bookmark(); - return; - } } ) .catch(console.error); diff --git a/packages/frontend/core/src/components/hooks/use-navigate-helper.ts b/packages/frontend/core/src/components/hooks/use-navigate-helper.ts index 55ebcd4fef..4da69c4166 100644 --- a/packages/frontend/core/src/components/hooks/use-navigate-helper.ts +++ b/packages/frontend/core/src/components/hooks/use-navigate-helper.ts @@ -1,3 +1,4 @@ +import { toURLSearchParams } from '@affine/core/utils'; import type { DocMode } from '@blocksuite/blocks'; import { createContext, useCallback, useContext, useMemo } from 'react'; import type { NavigateFunction, NavigateOptions } from 'react-router-dom'; @@ -45,11 +46,8 @@ export function useNavigateHelper() { elementIds?: string[], logic: RouteLogic = RouteLogic.PUSH ) => { - const search = new URLSearchParams(); - if (mode) search.append('mode', mode); - if (blockIds?.length) search.append('blockIds', blockIds.join(',')); - if (elementIds?.length) search.append('elementIds', elementIds.join(',')); - const query = search.size > 0 ? `?${search.toString()}` : ''; + const search = toURLSearchParams({ mode, blockIds, elementIds }); + const query = search?.size ? `?${search.toString()}` : ''; return navigate(`/workspace/${workspaceId}/${pageId}${query}`, { replace: logic === RouteLogic.REPLACE, }); diff --git a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx index e1fa0cc116..818bdd2178 100644 --- a/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx +++ b/packages/frontend/core/src/desktop/pages/workspace/share/share-page.tsx @@ -3,12 +3,14 @@ import { AppFallback } from '@affine/core/components/affine/app-container'; import { EditorOutlineViewer } from '@affine/core/components/blocksuite/outline-viewer'; import { useActiveBlocksuiteEditor } from '@affine/core/components/hooks/use-block-suite-editor'; import { usePageDocumentTitle } from '@affine/core/components/hooks/use-global-state'; +import { useNavigateHelper } from '@affine/core/components/hooks/use-navigate-helper'; import { PageDetailEditor } from '@affine/core/components/page-detail-editor'; import { SharePageNotFoundError } from '@affine/core/components/share-page-not-found-error'; import { AppContainer, MainContainer } from '@affine/core/components/workspace'; import { AuthService } from '@affine/core/modules/cloud'; import { type Editor, + type EditorSelector, EditorService, EditorsService, } from '@affine/core/modules/editor'; @@ -17,7 +19,12 @@ import { ShareReaderService } from '@affine/core/modules/share-doc'; import { CloudBlobStorage } from '@affine/core/modules/workspace-engine'; import { WorkspaceFlavour } from '@affine/env/workspace'; import { useI18n } from '@affine/i18n'; -import { type DocMode, DocModes } from '@blocksuite/blocks'; +import { + type DocMode, + DocModes, + RefNodeSlotsProvider, +} from '@blocksuite/blocks'; +import { DisposableGroup } from '@blocksuite/global/utils'; import { Logo1Icon } from '@blocksuite/icons/rc'; import type { AffineEditorContainer } from '@blocksuite/presets'; import type { Doc, Workspace } from '@toeverything/infra'; @@ -57,16 +64,29 @@ export const SharePage = ({ const location = useLocation(); - const { mode, isTemplate, templateName, templateSnapshotUrl } = + const { mode, selector, isTemplate, templateName, templateSnapshotUrl } = useMemo(() => { const searchParams = new URLSearchParams(location.search); const queryStringMode = searchParams.get('mode') as DocMode | null; + const blockIds = searchParams + .get('blockIds') + ?.split(',') + .filter(v => v.length); + const elementIds = searchParams + .get('elementIds') + ?.split(',') + .filter(v => v.length); return { mode: queryStringMode && DocModes.includes(queryStringMode) ? queryStringMode : null, + selector: { + blockIds, + elementIds, + refreshKey: searchParams.get('refreshKey') || undefined, + }, isTemplate: searchParams.has('isTemplate'), templateName: searchParams.get('templateName') || '', templateSnapshotUrl: searchParams.get('snapshotUrl') || '', @@ -94,6 +114,7 @@ export const SharePage = ({ workspaceBinary={data.workspaceBinary} docBinary={data.docBinary} publishMode={mode || data.publishMode} + selector={selector} isTemplate={isTemplate} templateName={templateName} templateSnapshotUrl={templateSnapshotUrl} @@ -110,6 +131,7 @@ const SharePageInner = ({ workspaceBinary, docBinary, publishMode = 'page' as DocMode, + selector, isTemplate, templateName, templateSnapshotUrl, @@ -119,6 +141,7 @@ const SharePageInner = ({ workspaceBinary: Uint8Array; docBinary: Uint8Array; publishMode?: DocMode; + selector?: EditorSelector; isTemplate?: boolean; templateName?: string; templateSnapshotUrl?: string; @@ -180,6 +203,10 @@ const SharePageInner = ({ const editor = doc.scope.get(EditorsService).createEditor(); editor.setMode(publishMode); + if (selector) { + editor.setSelector(selector); + } + setEditor(editor); }) .catch(err => { @@ -190,11 +217,13 @@ const SharePageInner = ({ workspaceId, workspacesService, publishMode, + selector, workspaceBinary, docBinary, ]); const pageTitle = useLiveData(page?.title$); + const { jumpToPageBlock, openPage } = useNavigateHelper(); usePageDocumentTitle(pageTitle); @@ -209,12 +238,35 @@ const SharePageInner = ({ editorContainer, (editorContainer as any).docTitle ); + + const disposable = new DisposableGroup(); + const refNodeSlots = + editorContainer.host?.std.getOptional(RefNodeSlotsProvider); + if (refNodeSlots) { + disposable.add( + refNodeSlots.docLinkClicked.on(({ pageId, params }) => { + if (params) { + const { mode, blockIds, elementIds } = params; + return jumpToPageBlock( + workspaceId, + pageId, + mode, + blockIds, + elementIds + ); + } + + return openPage(workspaceId, pageId); + }) + ); + } + return () => { unbind(); editor.setEditorContainer(null); }; }, - [editor, setActiveBlocksuiteEditor] + [editor, setActiveBlocksuiteEditor, jumpToPageBlock, openPage, workspaceId] ); if (!workspace || !page || !editor) { diff --git a/packages/frontend/core/src/modules/workbench/entities/workbench.ts b/packages/frontend/core/src/modules/workbench/entities/workbench.ts index 5711a1a745..59d9d8f1d5 100644 --- a/packages/frontend/core/src/modules/workbench/entities/workbench.ts +++ b/packages/frontend/core/src/modules/workbench/entities/workbench.ts @@ -1,7 +1,9 @@ +import { toURLSearchParams } from '@affine/core/utils'; import { Unreachable } from '@affine/env/constant'; -import type { DocMode } from '@blocksuite/blocks'; +import type { ReferenceParams } from '@blocksuite/blocks'; import { Entity, LiveData } from '@toeverything/infra'; import { type To } from 'history'; +import { omit } from 'lodash-es'; import { nanoid } from 'nanoid'; import type { WorkbenchNewTabHandler } from '../services/workbench-new-tab-handler'; @@ -122,14 +124,7 @@ export class Workbench extends Entity { } openDoc( - id: - | string - | { - docId: string; - mode?: DocMode; - blockIds?: string[]; - elementIds?: string[]; - }, + id: string | ({ docId: string } & ReferenceParams), options?: WorkbenchOpenOptions ) { const isString = typeof id === 'string'; @@ -137,12 +132,10 @@ export class Workbench extends Entity { let query = ''; if (!isString) { - const { mode, blockIds, elementIds } = id; - const search = new URLSearchParams(); - if (mode) search.set('mode', mode); - if (blockIds?.length) search.set('blockIds', blockIds.join(',')); - if (elementIds?.length) search.set('elementIds', elementIds.join(',')); - if (search.size > 0) query = `?${search.toString()}`; + const search = toURLSearchParams(omit(id, ['docId'])); + if (search?.size) { + query = `?${search.toString()}`; + } } this.open(`/${docId}${query}`, options);