mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-25 14:28:51 +08:00
fix(editor): paste surface-ref block to another doc as embed-linked-doc block (#10274)
[BS-2155](https://linear.app/affine-design/issue/BS-2155/复制-insert-frame-group-粘贴后,应当变为-block-ref-link)
This commit is contained in:
@@ -1,3 +1,4 @@
|
|||||||
export * from './code';
|
export * from './code';
|
||||||
export * from './copy';
|
export * from './copy';
|
||||||
export * from './paste';
|
export * from './paste';
|
||||||
|
export * from './surface-ref-to-embed';
|
||||||
|
|||||||
+6
-2
@@ -17,12 +17,16 @@ export const surfaceRefToEmbed =
|
|||||||
payload.snapshot.flavour === 'affine:surface-ref' &&
|
payload.snapshot.flavour === 'affine:surface-ref' &&
|
||||||
!std.store.hasBlock(payload.snapshot.id)
|
!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.id = std.workspace.idGenerator();
|
||||||
payload.snapshot.flavour = 'affine:embed-linked-doc';
|
payload.snapshot.flavour = 'affine:embed-linked-doc';
|
||||||
payload.snapshot.props = {
|
payload.snapshot.props = {
|
||||||
blockId: id,
|
|
||||||
pageId,
|
pageId,
|
||||||
|
params: {
|
||||||
|
mode: 'page',
|
||||||
|
blockIds: [blockId],
|
||||||
|
},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
@@ -12,6 +12,7 @@ import {
|
|||||||
NoteBlockModel,
|
NoteBlockModel,
|
||||||
RootBlockModel,
|
RootBlockModel,
|
||||||
} from '@blocksuite/affine-model';
|
} from '@blocksuite/affine-model';
|
||||||
|
import { surfaceRefToEmbed } from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
BLOCK_CHILDREN_CONTAINER_PADDING_LEFT,
|
||||||
EMBED_CARD_HEIGHT,
|
EMBED_CARD_HEIGHT,
|
||||||
@@ -68,7 +69,6 @@ import { PreviewHelper } from '../helpers/preview-helper.js';
|
|||||||
import { gfxBlocksFilter } from '../middleware/blocks-filter.js';
|
import { gfxBlocksFilter } from '../middleware/blocks-filter.js';
|
||||||
import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js';
|
import { newIdCrossDoc } from '../middleware/new-id-cross-doc.js';
|
||||||
import { reorderList } from '../middleware/reorder-list';
|
import { reorderList } from '../middleware/reorder-list';
|
||||||
import { surfaceRefToEmbed } from '../middleware/surface-ref-to-embed.js';
|
|
||||||
import {
|
import {
|
||||||
containBlock,
|
containBlock,
|
||||||
extractIdsFromSnapshot,
|
extractIdsFromSnapshot,
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
import { deleteTextCommand } from '@blocksuite/affine-components/rich-text';
|
import { deleteTextCommand } from '@blocksuite/affine-components/rich-text';
|
||||||
|
import {
|
||||||
|
pasteMiddleware,
|
||||||
|
surfaceRefToEmbed,
|
||||||
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
clearAndSelectFirstModelCommand,
|
clearAndSelectFirstModelCommand,
|
||||||
deleteSelectedModelsCommand,
|
deleteSelectedModelsCommand,
|
||||||
@@ -13,6 +17,7 @@ import type { UIEventHandler } from '@blocksuite/block-std';
|
|||||||
import { DisposableGroup } from '@blocksuite/global/utils';
|
import { DisposableGroup } from '@blocksuite/global/utils';
|
||||||
import type { BlockSnapshot, Store } from '@blocksuite/store';
|
import type { BlockSnapshot, Store } from '@blocksuite/store';
|
||||||
|
|
||||||
|
import { replaceIdMiddleware } from '../../_common/transformers/middlewares';
|
||||||
import { ReadOnlyClipboard } from './readonly-clipboard';
|
import { ReadOnlyClipboard } from './readonly-clipboard';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -22,6 +27,23 @@ import { ReadOnlyClipboard } from './readonly-clipboard';
|
|||||||
export class PageClipboard extends ReadOnlyClipboard {
|
export class PageClipboard extends ReadOnlyClipboard {
|
||||||
protected _init = () => {
|
protected _init = () => {
|
||||||
this._initAdapters();
|
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 (
|
onBlockSnapshotPaste = async (
|
||||||
@@ -143,3 +165,5 @@ export class PageClipboard extends ReadOnlyClipboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export { pasteMiddleware };
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ import {
|
|||||||
ImageAdapter,
|
ImageAdapter,
|
||||||
MixTextAdapter,
|
MixTextAdapter,
|
||||||
NotionTextAdapter,
|
NotionTextAdapter,
|
||||||
pasteMiddleware,
|
|
||||||
} from '@blocksuite/affine-shared/adapters';
|
} from '@blocksuite/affine-shared/adapters';
|
||||||
import {
|
import {
|
||||||
copySelectedModelsCommand,
|
copySelectedModelsCommand,
|
||||||
@@ -17,7 +16,6 @@ import { DisposableGroup } from '@blocksuite/global/utils';
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
defaultImageProxyMiddleware,
|
defaultImageProxyMiddleware,
|
||||||
replaceIdMiddleware,
|
|
||||||
titleMiddleware,
|
titleMiddleware,
|
||||||
} from '../../_common/transformers/middlewares.js';
|
} from '../../_common/transformers/middlewares.js';
|
||||||
import { ClipboardAdapter } from './adapter.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('text/plain', MixTextAdapter, 70);
|
||||||
this._std.clipboard.registerAdapter('*/*', AttachmentAdapter, 60);
|
this._std.clipboard.registerAdapter('*/*', AttachmentAdapter, 60);
|
||||||
const copy = copyMiddleware(this._std);
|
const copy = copyMiddleware(this._std);
|
||||||
const paste = pasteMiddleware(this._std);
|
|
||||||
this._std.clipboard.use(copy);
|
this._std.clipboard.use(copy);
|
||||||
this._std.clipboard.use(paste);
|
|
||||||
this._std.clipboard.use(
|
|
||||||
replaceIdMiddleware(this._std.store.workspace.idGenerator)
|
|
||||||
);
|
|
||||||
this._std.clipboard.use(
|
this._std.clipboard.use(
|
||||||
titleMiddleware(this._std.store.workspace.meta.docMetas)
|
titleMiddleware(this._std.store.workspace.meta.docMetas)
|
||||||
);
|
);
|
||||||
@@ -91,10 +84,6 @@ export class ReadOnlyClipboard {
|
|||||||
this._std.clipboard.unregisterAdapter('text/html');
|
this._std.clipboard.unregisterAdapter('text/html');
|
||||||
this._std.clipboard.unregisterAdapter('*/*');
|
this._std.clipboard.unregisterAdapter('*/*');
|
||||||
this._std.clipboard.unuse(copy);
|
this._std.clipboard.unuse(copy);
|
||||||
this._std.clipboard.unuse(paste);
|
|
||||||
this._std.clipboard.unuse(
|
|
||||||
replaceIdMiddleware(this._std.store.workspace.idGenerator)
|
|
||||||
);
|
|
||||||
this._std.clipboard.unuse(
|
this._std.clipboard.unuse(
|
||||||
titleMiddleware(this._std.store.workspace.meta.docMetas)
|
titleMiddleware(this._std.store.workspace.meta.docMetas)
|
||||||
);
|
);
|
||||||
@@ -135,4 +124,4 @@ export class ReadOnlyClipboard {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export { copyMiddleware, pasteMiddleware };
|
export { copyMiddleware };
|
||||||
|
|||||||
@@ -1,5 +1,10 @@
|
|||||||
import { test } from '@affine-test/kit/playwright';
|
import { test } from '@affine-test/kit/playwright';
|
||||||
import { pasteContent } from '@affine-test/kit/utils/clipboard';
|
import { pasteContent } from '@affine-test/kit/utils/clipboard';
|
||||||
|
import {
|
||||||
|
clickEdgelessModeButton,
|
||||||
|
clickPageModeButton,
|
||||||
|
locateEditorContainer,
|
||||||
|
} from '@affine-test/kit/utils/editor';
|
||||||
import {
|
import {
|
||||||
copyByKeyboard,
|
copyByKeyboard,
|
||||||
pasteByKeyboard,
|
pasteByKeyboard,
|
||||||
@@ -8,6 +13,7 @@ import {
|
|||||||
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
import { openHomePage } from '@affine-test/kit/utils/load-page';
|
||||||
import {
|
import {
|
||||||
clickNewPageButton,
|
clickNewPageButton,
|
||||||
|
getBlockSuiteEditorTitle,
|
||||||
type,
|
type,
|
||||||
waitForEditorLoad,
|
waitForEditorLoad,
|
||||||
} from '@affine-test/kit/utils/page-logic';
|
} 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');
|
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');
|
||||||
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user