diff --git a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx index f8fab4519..d267d89f7 100644 --- a/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx +++ b/packages/frontend/core/src/components/blocksuite/block-suite-editor/blocksuite-editor-container.tsx @@ -1,10 +1,4 @@ -import { ViewService } from '@affine/core/modules/workbench/services/view'; -import type { - BaseSelection, - BlockElement, - TextSelection, -} from '@blocksuite/block-std'; -import type { Disposable } from '@blocksuite/global/utils'; +import type { BlockElement } from '@blocksuite/block-std'; import type { AffineEditorContainer, EdgelessEditor, @@ -12,7 +6,7 @@ import type { } from '@blocksuite/presets'; import type { Doc } from '@blocksuite/store'; import { Slot } from '@blocksuite/store'; -import { type DocMode, useServiceOptional } from '@toeverything/infra'; +import { type DocMode } from '@toeverything/infra'; import clsx from 'clsx'; import type React from 'react'; import type { RefObject } from 'react'; @@ -111,7 +105,6 @@ export const BlocksuiteEditorContainer = forwardRef< const rootRef = useRef(null); const docRef = useRef(null); const edgelessRef = useRef(null); - const renderStartRef = useRef(Date.now()); const slots: BlocksuiteEditorContainerRef['slots'] = useMemo(() => { return { @@ -214,7 +207,6 @@ export const BlocksuiteEditorContainer = forwardRef< }, [affineEditorContainerProxy, ref]); const blockElement = useBlockElementById(rootRef, defaultSelectedBlockId); - const currentView = useServiceOptional(ViewService)?.view; useEffect(() => { let canceled = false; @@ -253,68 +245,6 @@ export const BlocksuiteEditorContainer = forwardRef< }; }, [blockElement, affineEditorContainerProxy, mode]); - useEffect(() => { - let disposable: Disposable | null = null; - let canceled = false; - // Function to handle block selection change - const handleSelectionChange = (selection: BaseSelection[]) => { - const viewLocation = currentView?.location$.value; - const currentPath = viewLocation?.pathname; - const locationHash = viewLocation?.hash; - if ( - !currentView || - !currentPath || - // do not update the hash during the initial render - renderStartRef.current > Date.now() - 1000 - ) { - return; - } - if (mode === 'edgeless') { - if (locationHash) { - currentView.replace(currentPath); - } - return; - } - - if (selection[0]?.type === 'text') { - const textSelection = selection[0] as TextSelection; - if (textSelection.from.length === 0) { - // Clear the hash if no block is selected - if (locationHash) { - currentView.replace(currentPath); - } - return; - } - } - - const selectedId = selection[0]?.blockId; - if (!selectedId) { - return; - } - const newHash = `#${selectedId}`; - - // Only update the hash if it has changed - if (locationHash !== newHash) { - hashChangedRef.current = true; - currentView.replace(currentPath + newHash); - } - }; - - affineEditorContainerProxy.updateComplete - .then(() => { - const selectManager = affineEditorContainerProxy.host?.selection; - if (!selectManager || canceled) return; - // Set up the new disposable listener - disposable = selectManager.slots.changed.on(handleSelectionChange); - }) - .catch(console.error); - - return () => { - canceled = true; - disposable?.dispose(); - }; - }, [affineEditorContainerProxy, currentView, mode]); - return (
{ +const generateUrl = ({ + workspaceId, + pageId, + urlType, + blockId, +}: UseSharingUrl & { blockId?: string }) => { // to generate a private url like https://app.affine.app/workspace/123/456 // or https://app.affine.app/workspace/123/456#block-123 // to generate a public url like https://app.affine.app/share/123/456 - // or https://app.affine.app/share/123/456?mode=edgeless - - const workbench = useService(WorkbenchService).workbench; - const activeView = useLiveData(workbench.activeView$); - const hash = useLiveData(activeView.location$).hash; - const baseUrl = getAffineCloudBaseUrl(); - const url = useMemo(() => { - // baseUrl is null when running in electron and without network - if (!baseUrl) return null; + if (!baseUrl) return null; - try { - return new URL( - `${baseUrl}/${urlType}/${workspaceId}/${pageId}${urlType === 'workspace' && hash ? `${hash}` : ''}` - ).toString(); - } catch (e) { - return null; - } - }, [baseUrl, hash, pageId, urlType, workspaceId]); - - return url; + try { + return new URL( + `${baseUrl}/${urlType}/${workspaceId}/${pageId}${urlType === 'workspace' && blockId ? `#${blockId}` : ''}` + ).toString(); + } catch (e) { + return null; + } }; export const useSharingUrl = ({ @@ -48,7 +43,18 @@ export const useSharingUrl = ({ urlType, }: UseSharingUrl) => { const t = useI18n(); - const sharingUrl = useGenerateUrl({ workspaceId, pageId, urlType }); + const [blockId, setBlockId] = useState(''); + const [editor] = useActiveBlocksuiteEditor(); + const sharingUrl = useMemo( + () => + generateUrl({ + workspaceId, + pageId, + urlType, + blockId: blockId.length > 0 ? blockId : undefined, + }), + [workspaceId, pageId, urlType, blockId] + ); const onClickCopyLink = useCallback(() => { if (sharingUrl) { @@ -73,6 +79,34 @@ export const useSharingUrl = ({ } }, [sharingUrl, t, urlType]); + useEffect(() => { + let disposable: Disposable | null = null; + const selectManager = editor?.host?.selection; + if (urlType !== 'workspace' || !selectManager) { + return; + } + + // if the block is already selected, set the blockId + const currentBlockSelection = selectManager.find('block'); + if (currentBlockSelection) { + setBlockId(`#${currentBlockSelection.blockId}`); + } + + disposable = selectManager.slots.changed.on(selections => { + setBlockId(prev => { + if (selections[0] && selections[0].type === 'block') { + return `#${selections[0].blockId}`; + } else if (prev.length > 0) { + return ''; + } else { + return prev; + } + }); + }); + return () => { + disposable?.dispose(); + }; + }, [editor?.host?.selection, urlType]); return { sharingUrl, onClickCopyLink,