diff --git a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx
index 2bc67551d9..1f4ad2ca45 100644
--- a/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx
+++ b/packages/frontend/core/src/components/affine/share-page-modal/share-menu/share-page.tsx
@@ -2,7 +2,10 @@ import { notify, Skeleton } from '@affine/component';
import { Button } from '@affine/component/ui/button';
import { Menu, MenuItem, MenuTrigger } from '@affine/component/ui/menu';
import { openSettingModalAtom } from '@affine/core/atoms';
-import { useSharingUrl } from '@affine/core/hooks/affine/use-share-url';
+import {
+ getSelectedNodes,
+ useSharingUrl,
+} from '@affine/core/hooks/affine/use-share-url';
import { useAsyncCallback } from '@affine/core/hooks/affine-async-hooks';
import { track } from '@affine/core/mixpanel';
import { ServerConfigService } from '@affine/core/modules/cloud';
@@ -26,7 +29,7 @@ import {
import { useLiveData, useService } from '@toeverything/infra';
import { cssVar } from '@toeverything/theme';
import { useSetAtom } from 'jotai';
-import { Suspense, useCallback, useEffect } from 'react';
+import { Suspense, useCallback, useEffect, useMemo } from 'react';
import { ErrorBoundary } from 'react-error-boundary';
import { CloudSvg } from '../cloud-svg';
@@ -65,6 +68,8 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
workspaceMetadata: { id: workspaceId },
} = props;
const editor = useService(EditorService).editor;
+ const currentMode = useLiveData(editor.mode$);
+ const editorContainer = useLiveData(editor.editorContainer$);
const shareInfoService = useService(ShareInfoService);
const serverConfig = useService(ServerConfigService).serverConfig;
useEffect(() => {
@@ -153,6 +158,10 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
const isMac = environment.isMacOs;
+ const { blockIds, elementIds } = useMemo(
+ () => getSelectedNodes(editorContainer?.host || null, currentMode),
+ [editorContainer, currentMode]
+ );
const { onClickCopyLink } = useSharingUrl({
workspaceId,
pageId: editor.doc.id,
@@ -165,9 +174,8 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
onClickCopyLink('edgeless' as DocMode);
}, [onClickCopyLink]);
const onCopyBlockLink = useCallback(() => {
- // TODO(@JimmFly): handle frame
- onClickCopyLink();
- }, [onClickCopyLink]);
+ onClickCopyLink(currentMode, blockIds, elementIds);
+ }, [currentMode, onClickCopyLink, blockIds, elementIds]);
if (isLoading) {
// TODO(@eyhn): loading and error UI
@@ -286,7 +294,11 @@ export const AFFiNESharePage = (props: ShareMenuProps) => {
>
{t['com.affine.share-menu.copy.edgeless']()}
- } onSelect={onCopyBlockLink}>
+ }
+ onSelect={onCopyBlockLink}
+ disabled={blockIds.length + elementIds.length === 0}
+ >
{t['com.affine.share-menu.copy.block']()}
>
diff --git a/packages/frontend/core/src/hooks/affine/use-share-url.ts b/packages/frontend/core/src/hooks/affine/use-share-url.ts
index 362e629e7e..1b852bb71a 100644
--- a/packages/frontend/core/src/hooks/affine/use-share-url.ts
+++ b/packages/frontend/core/src/hooks/affine/use-share-url.ts
@@ -2,12 +2,11 @@ import { notify } from '@affine/component';
import { track } from '@affine/core/mixpanel';
import { getAffineCloudBaseUrl } from '@affine/core/modules/cloud/services/fetch';
import { useI18n } from '@affine/i18n';
-import type { BaseSelection } from '@blocksuite/block-std';
-import type { DocMode } from '@blocksuite/blocks';
+import { type EditorHost } from '@blocksuite/block-std';
+import { GfxBlockElementModel } from '@blocksuite/block-std/gfx';
+import type { DocMode, EdgelessRootService } from '@blocksuite/blocks';
import { useCallback } from 'react';
-import { useActiveBlocksuiteEditor } from '../use-block-suite-editor';
-
export type UseSharingUrl = {
workspaceId: string;
pageId: string;
@@ -18,7 +17,9 @@ export type UseSharingUrl = {
};
/**
- * to generate a url like https://app.affine.pro/workspace/workspaceId/docId?mode=DocMode?element=seletedBlockid#seletedBlockid
+ * To generate a url like
+ *
+ * https://app.affine.pro/workspace/workspaceId/docId?mode=DocMode&elementIds=seletedElementIds&blockIds=selectedBlockIds
*/
export const generateUrl = ({
workspaceId,
@@ -75,29 +76,72 @@ const getShareLinkType = ({
}
};
-const getSelectionIds = (selections?: BaseSelection[]) => {
- if (!selections || selections.length === 0) {
- return { blockIds: [], elementIds: [] };
- }
+export const getSelectedNodes = (
+ host: EditorHost | null,
+ mode: DocMode = 'page'
+) => {
+ const std = host?.std;
const blockIds: string[] = [];
const elementIds: string[] = [];
- // TODO(@JimmFly): handle multiple selections and elementIds
- if (selections[0].type === 'block') {
- blockIds.push(selections[0].blockId);
+ const result = { blockIds, elementIds };
+
+ if (!std) {
+ return result;
}
- return { blockIds, elementIds };
+
+ if (mode === 'edgeless') {
+ const service = std.getService('affine:page');
+ if (!service) return result;
+
+ for (const element of service.selection.selectedElements) {
+ if (element instanceof GfxBlockElementModel) {
+ blockIds.push(element.id);
+ } else {
+ elementIds.push(element.id);
+ }
+ }
+
+ return result;
+ }
+
+ const [success, ctx] = std.command
+ .chain()
+ .tryAll(chain => [
+ chain.getTextSelection(),
+ chain.getBlockSelections(),
+ chain.getImageSelections(),
+ ])
+ .getSelectedModels({
+ mode: 'highest',
+ })
+ .run();
+
+ if (!success) {
+ return result;
+ }
+
+ // should return an empty array if `to` of the range is null
+ if (
+ ctx.currentTextSelection &&
+ !ctx.currentTextSelection.to &&
+ ctx.currentTextSelection.from.length === 0
+ ) {
+ return result;
+ }
+
+ if (ctx.selectedModels?.length) {
+ blockIds.push(...ctx.selectedModels.map(model => model.id));
+ return result;
+ }
+
+ return result;
};
export const useSharingUrl = ({ workspaceId, pageId }: UseSharingUrl) => {
const t = useI18n();
- const [editor] = useActiveBlocksuiteEditor();
const onClickCopyLink = useCallback(
- (shareMode?: DocMode) => {
- const selectManager = editor?.host?.selection;
- const selections = selectManager?.value;
- const { blockIds, elementIds } = getSelectionIds(selections);
-
+ (shareMode?: DocMode, blockIds?: string[], elementIds?: string[]) => {
const sharingUrl = generateUrl({
workspaceId,
pageId,
@@ -130,8 +174,9 @@ export const useSharingUrl = ({ workspaceId, pageId }: UseSharingUrl) => {
});
}
},
- [editor, pageId, t, workspaceId]
+ [pageId, t, workspaceId]
);
+
return {
onClickCopyLink,
};