From 319d909ac86ac5c45e8f848e26f1dfd080fbdfe7 Mon Sep 17 00:00:00 2001 From: donteatfriedrice Date: Wed, 19 Feb 2025 07:02:26 +0000 Subject: [PATCH] fix(editor): paste surface-ref block to another doc as embed-linked-doc block (#10274) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [BS-2155](https://linear.app/affine-design/issue/BS-2155/复制-insert-frame-group-粘贴后,应当变为-block-ref-link) --- .../shared/src/adapters/middlewares/index.ts | 1 + .../middlewares}/surface-ref-to-embed.ts | 8 ++- .../src/watchers/drag-event-watcher.ts | 2 +- .../root-block/clipboard/page-clipboard.ts | 24 +++++++ .../clipboard/readonly-clipboard.ts | 13 +--- .../blocksuite/clipboard/clipboard.spec.ts | 65 +++++++++++++++++++ 6 files changed, 98 insertions(+), 15 deletions(-) rename blocksuite/affine/{widget-drag-handle/src/middleware => shared/src/adapters/middlewares}/surface-ref-to-embed.ts (81%) diff --git a/blocksuite/affine/shared/src/adapters/middlewares/index.ts b/blocksuite/affine/shared/src/adapters/middlewares/index.ts index 44e3cd8fbf..c576d4a835 100644 --- a/blocksuite/affine/shared/src/adapters/middlewares/index.ts +++ b/blocksuite/affine/shared/src/adapters/middlewares/index.ts @@ -1,3 +1,4 @@ export * from './code'; export * from './copy'; export * from './paste'; +export * from './surface-ref-to-embed'; diff --git a/blocksuite/affine/widget-drag-handle/src/middleware/surface-ref-to-embed.ts b/blocksuite/affine/shared/src/adapters/middlewares/surface-ref-to-embed.ts similarity index 81% rename from blocksuite/affine/widget-drag-handle/src/middleware/surface-ref-to-embed.ts rename to blocksuite/affine/shared/src/adapters/middlewares/surface-ref-to-embed.ts index e4b2885281..6c3951b476 100644 --- a/blocksuite/affine/widget-drag-handle/src/middleware/surface-ref-to-embed.ts +++ b/blocksuite/affine/shared/src/adapters/middlewares/surface-ref-to-embed.ts @@ -17,12 +17,16 @@ export const surfaceRefToEmbed = payload.snapshot.flavour === 'affine:surface-ref' && !std.store.hasBlock(payload.snapshot.id) ) { - const id = payload.snapshot.id; + // The blockId of the original surface-ref block + const blockId = payload.snapshot.id; payload.snapshot.id = std.workspace.idGenerator(); payload.snapshot.flavour = 'affine:embed-linked-doc'; payload.snapshot.props = { - blockId: id, pageId, + params: { + mode: 'page', + blockIds: [blockId], + }, }; } }); diff --git a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts index f544ca9f28..c1078daee8 100644 --- a/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts +++ b/blocksuite/affine/widget-drag-handle/src/watchers/drag-event-watcher.ts @@ -12,6 +12,7 @@ import { NoteBlockModel, RootBlockModel, } from '@blocksuite/affine-model'; +import { surfaceRefToEmbed } from '@blocksuite/affine-shared/adapters'; import { BLOCK_CHILDREN_CONTAINER_PADDING_LEFT, EMBED_CARD_HEIGHT, @@ -68,7 +69,6 @@ import { PreviewHelper } from '../helpers/preview-helper.js'; import { gfxBlocksFilter } from '../middleware/blocks-filter.js'; import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js'; import { reorderList } from '../middleware/reorder-list'; -import { surfaceRefToEmbed } from '../middleware/surface-ref-to-embed.js'; import { containBlock, extractIdsFromSnapshot, diff --git a/blocksuite/blocks/src/root-block/clipboard/page-clipboard.ts b/blocksuite/blocks/src/root-block/clipboard/page-clipboard.ts index 3ea40af695..162c4a032e 100644 --- a/blocksuite/blocks/src/root-block/clipboard/page-clipboard.ts +++ b/blocksuite/blocks/src/root-block/clipboard/page-clipboard.ts @@ -1,4 +1,8 @@ import { deleteTextCommand } from '@blocksuite/affine-components/rich-text'; +import { + pasteMiddleware, + surfaceRefToEmbed, +} from '@blocksuite/affine-shared/adapters'; import { clearAndSelectFirstModelCommand, deleteSelectedModelsCommand, @@ -13,6 +17,7 @@ import type { UIEventHandler } from '@blocksuite/block-std'; import { DisposableGroup } from '@blocksuite/global/utils'; import type { BlockSnapshot, Store } from '@blocksuite/store'; +import { replaceIdMiddleware } from '../../_common/transformers/middlewares'; import { ReadOnlyClipboard } from './readonly-clipboard'; /** @@ -22,6 +27,23 @@ import { ReadOnlyClipboard } from './readonly-clipboard'; export class PageClipboard extends ReadOnlyClipboard { protected _init = () => { this._initAdapters(); + const paste = pasteMiddleware(this._std); + // Use surfaceRefToEmbed middleware to convert surface-ref to embed-linked-doc + // When pastina a surface-ref block to another doc + const surfaceRefToEmbedMiddleware = surfaceRefToEmbed(this._std); + const replaceId = replaceIdMiddleware( + this._std.store.workspace.idGenerator + ); + this._std.clipboard.use(paste); + this._std.clipboard.use(surfaceRefToEmbedMiddleware); + this._std.clipboard.use(replaceId); + this._disposables.add({ + dispose: () => { + this._std.clipboard.unuse(paste); + this._std.clipboard.unuse(surfaceRefToEmbedMiddleware); + this._std.clipboard.unuse(replaceId); + }, + }); }; onBlockSnapshotPaste = async ( @@ -143,3 +165,5 @@ export class PageClipboard extends ReadOnlyClipboard { } } } + +export { pasteMiddleware }; diff --git a/blocksuite/blocks/src/root-block/clipboard/readonly-clipboard.ts b/blocksuite/blocks/src/root-block/clipboard/readonly-clipboard.ts index f031f8f02c..f6c31fa233 100644 --- a/blocksuite/blocks/src/root-block/clipboard/readonly-clipboard.ts +++ b/blocksuite/blocks/src/root-block/clipboard/readonly-clipboard.ts @@ -5,7 +5,6 @@ import { ImageAdapter, MixTextAdapter, NotionTextAdapter, - pasteMiddleware, } from '@blocksuite/affine-shared/adapters'; import { copySelectedModelsCommand, @@ -17,7 +16,6 @@ import { DisposableGroup } from '@blocksuite/global/utils'; import { defaultImageProxyMiddleware, - replaceIdMiddleware, titleMiddleware, } from '../../_common/transformers/middlewares.js'; import { ClipboardAdapter } from './adapter.js'; @@ -64,12 +62,7 @@ export class ReadOnlyClipboard { this._std.clipboard.registerAdapter('text/plain', MixTextAdapter, 70); this._std.clipboard.registerAdapter('*/*', AttachmentAdapter, 60); const copy = copyMiddleware(this._std); - const paste = pasteMiddleware(this._std); this._std.clipboard.use(copy); - this._std.clipboard.use(paste); - this._std.clipboard.use( - replaceIdMiddleware(this._std.store.workspace.idGenerator) - ); this._std.clipboard.use( titleMiddleware(this._std.store.workspace.meta.docMetas) ); @@ -91,10 +84,6 @@ export class ReadOnlyClipboard { this._std.clipboard.unregisterAdapter('text/html'); this._std.clipboard.unregisterAdapter('*/*'); this._std.clipboard.unuse(copy); - this._std.clipboard.unuse(paste); - this._std.clipboard.unuse( - replaceIdMiddleware(this._std.store.workspace.idGenerator) - ); this._std.clipboard.unuse( titleMiddleware(this._std.store.workspace.meta.docMetas) ); @@ -135,4 +124,4 @@ export class ReadOnlyClipboard { } } -export { copyMiddleware, pasteMiddleware }; +export { copyMiddleware }; diff --git a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts index feb5c9acb8..106d87af8b 100644 --- a/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts +++ b/tests/affine-local/e2e/blocksuite/clipboard/clipboard.spec.ts @@ -1,5 +1,10 @@ import { test } from '@affine-test/kit/playwright'; import { pasteContent } from '@affine-test/kit/utils/clipboard'; +import { + clickEdgelessModeButton, + clickPageModeButton, + locateEditorContainer, +} from '@affine-test/kit/utils/editor'; import { copyByKeyboard, pasteByKeyboard, @@ -8,6 +13,7 @@ import { import { openHomePage } from '@affine-test/kit/utils/load-page'; import { clickNewPageButton, + getBlockSuiteEditorTitle, type, waitForEditorLoad, } from '@affine-test/kit/utils/page-logic'; @@ -141,3 +147,62 @@ test.describe('paste in multiple blocks text selection', () => { await verifyParagraphContent(page, 3, 'test world'); }); }); + +test('paste surface-ref block to another doc as embed-linked-doc block', async ({ + page, +}) => { + await openHomePage(page); + await clickNewPageButton(page, 'Clipboard Test'); + await waitForEditorLoad(page); + await clickEdgelessModeButton(page); + const container = locateEditorContainer(page); + await container.click(); + + // add a shape + await page.keyboard.press('s'); + // click to add a shape + await container.click({ position: { x: 100, y: 500 } }); + await page.waitForTimeout(50); + // add a frame + await page.keyboard.press('f'); + await page.waitForTimeout(50); + + // click on the frame title to trigger the change frame button toolbar + const frameTitle = page.locator('affine-frame-title'); + await frameTitle.click(); + await page.waitForTimeout(50); + const changeFrameButton = page.locator('edgeless-change-frame-button'); + // get insert into page button which with aria-label 'Insert into Page' + const insertIntoPageButton = changeFrameButton.locator( + `editor-icon-button[aria-label="Insert into Page"]` + ); + await insertIntoPageButton.click(); + + await clickPageModeButton(page); + await page.waitForTimeout(50); + + // copy surface-ref block + const surfaceRefBlock = page.locator('.affine-surface-ref'); + await surfaceRefBlock.click(); + await page.waitForTimeout(50); + await copyByKeyboard(page); + + // paste to another doc + await clickNewPageButton(page); + await waitForEditorLoad(page); + const title2 = getBlockSuiteEditorTitle(page); + await title2.pressSequentially('page2'); + await page.keyboard.press('Enter'); + await page.waitForTimeout(50); + + // paste the surface-ref block + await pasteByKeyboard(page); + await page.waitForTimeout(50); + + const embedLinkedDocBlock = page.locator('affine-embed-linked-doc-block'); + await expect(embedLinkedDocBlock).toBeVisible(); + const embedLinkedDocBlockTitle = embedLinkedDocBlock.locator( + '.affine-embed-linked-doc-content-title-text' + ); + await expect(embedLinkedDocBlockTitle).toHaveText('Clipboard Test'); +});