From a459a00b218249c27b0cb651923d41dd5f3a6973 Mon Sep 17 00:00:00 2001 From: donteatfriedrice Date: Fri, 28 Mar 2025 02:25:00 +0000 Subject: [PATCH] feat(editor): support convert edgeless bookmark to embed iframe block (#11237) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Close [BS-2941](https://linear.app/affine-design/issue/BS-2941/白板-surface-中-card-view-无法转为-embed-view) --- .../block-bookmark/src/configs/toolbar.ts | 106 +++++++++++------- .../src/embed-iframe-block/index.ts | 5 + .../src/embed-iframe-block/utils.ts | 19 ++++ 3 files changed, 91 insertions(+), 39 deletions(-) diff --git a/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts b/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts index fa34768a4c..3ef41c44be 100644 --- a/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts +++ b/blocksuite/affine/blocks/block-bookmark/src/configs/toolbar.ts @@ -1,3 +1,8 @@ +import { + canEmbedAsIframe, + EMBED_IFRAME_DEFAULT_HEIGHT_IN_SURFACE, + EMBED_IFRAME_DEFAULT_WIDTH_IN_SURFACE, +} from '@blocksuite/affine-block-embed'; import { reassociateConnectorsCommand } from '@blocksuite/affine-block-surface'; import { toast } from '@blocksuite/affine-components/toast'; import { @@ -13,7 +18,6 @@ import { ActionPlacement, EmbedIframeService, EmbedOptionProvider, - FeatureFlagService, type LinkEventType, type ToolbarAction, type ToolbarActionGroup, @@ -145,26 +149,20 @@ const builtinToolbarConfig = { if (!model) return true; const url = model.props.url; - // check if the url can be embedded as iframe block - const featureFlag = ctx.std.get(FeatureFlagService); - const embedIframeService = ctx.std.get(EmbedIframeService); - const isEmbedIframeEnabled = featureFlag.getFlag( - 'enable_embed_iframe_block' - ); - const canEmbedAsIframe = - isEmbedIframeEnabled && embedIframeService.canEmbed(url); - + // check if the url can be embedded as iframe block or other embed blocks const options = ctx.std .get(EmbedOptionProvider) .getEmbedBlockOptions(url); - return !canEmbedAsIframe && options?.viewType !== 'embed'; + return ( + !canEmbedAsIframe(ctx.std, url) && options?.viewType !== 'embed' + ); }, run(ctx) { const model = ctx.getCurrentModelByType(BookmarkBlockModel); if (!model) return; - const { caption, url, style } = model.props; + const { caption, url, title, description, style } = model.props; const { parent } = model; const index = parent?.children.indexOf(model); if (!parent) return; @@ -172,14 +170,10 @@ const builtinToolbarConfig = { let blockId: string | undefined; // first try to embed as iframe block - const featureFlag = ctx.std.get(FeatureFlagService); - const isEmbedIframeEnabled = featureFlag.getFlag( - 'enable_embed_iframe_block' - ); - const embedIframeService = ctx.std.get(EmbedIframeService); - if (isEmbedIframeEnabled && embedIframeService.canEmbed(url)) { + if (canEmbedAsIframe(ctx.std, url)) { + const embedIframeService = ctx.std.get(EmbedIframeService); blockId = embedIframeService.addEmbedIframeBlock( - { url, caption }, + { url, caption, title, description }, parent.id, index ); @@ -201,6 +195,8 @@ const builtinToolbarConfig = { { url, caption, + title, + description, style: newStyle, }, parent, @@ -379,30 +375,62 @@ const builtinSurfaceToolbarConfig = { if (!model) return; const { id: oldId, xywh, parent } = model; - const { url, caption } = model.props; - const options = ctx.std - .get(EmbedOptionProvider) - .getEmbedBlockOptions(url); + const { url, caption, title, description } = model.props; - if (options?.viewType !== 'embed') return; + let newId: string | undefined; - const { flavour, styles } = options; - let { style } = model.props; + // first try to embed as iframe block + if (canEmbedAsIframe(ctx.std, url)) { + const embedIframeService = ctx.std.get(EmbedIframeService); + const config = embedIframeService.getConfig(url); + if (!config) { + return; + } - if (!styles.includes(style)) { - style = styles[0]; + const bound = Bound.deserialize(xywh); + const options = config.options; + const { widthInSurface, heightInSurface } = options ?? {}; + bound.w = widthInSurface ?? EMBED_IFRAME_DEFAULT_WIDTH_IN_SURFACE; + bound.h = + heightInSurface ?? EMBED_IFRAME_DEFAULT_HEIGHT_IN_SURFACE; + + newId = ctx.store.addBlock( + 'affine:embed-iframe', + { url, caption, title, description, xywh: bound.serialize() }, + parent + ); + } else { + const options = ctx.std + .get(EmbedOptionProvider) + .getEmbedBlockOptions(url); + + if (options?.viewType !== 'embed') return; + + const { flavour, styles } = options; + let { style } = model.props; + + if (!styles.includes(style)) { + style = styles[0]; + } + + const bound = Bound.deserialize(xywh); + bound.w = EMBED_CARD_WIDTH[style]; + bound.h = EMBED_CARD_HEIGHT[style]; + + newId = ctx.store.addBlock( + flavour, + { + url, + caption, + title, + description, + style, + xywh: bound.serialize(), + }, + parent + ); } - const bounds = Bound.deserialize(xywh); - bounds.w = EMBED_CARD_WIDTH[style]; - bounds.h = EMBED_CARD_HEIGHT[style]; - - const newId = ctx.store.addBlock( - flavour, - { url, caption, style, xywh: bounds.serialize() }, - parent - ); - ctx.command.exec(reassociateConnectorsCommand, { oldId, newId }); ctx.store.deleteBlock(model); @@ -427,7 +455,7 @@ const builtinSurfaceToolbarConfig = { .get(EmbedOptionProvider) .getEmbedBlockOptions(url); - return options?.viewType === 'embed'; + return canEmbedAsIframe(ctx.std, url) || options?.viewType === 'embed'; }, content(ctx) { const model = ctx.getCurrentModelByType(BookmarkBlockModel); diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/index.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/index.ts index 2e2c39f7eb..26df090728 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/index.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/index.ts @@ -1,5 +1,10 @@ export * from './adapters'; export * from './commands'; export * from './configs'; +export { + EMBED_IFRAME_DEFAULT_HEIGHT_IN_SURFACE, + EMBED_IFRAME_DEFAULT_WIDTH_IN_SURFACE, +} from './consts'; export * from './embed-iframe-block'; export * from './embed-iframe-spec'; +export { canEmbedAsIframe } from './utils'; diff --git a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/utils.ts b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/utils.ts index f33c572c1b..6ff5c36aa4 100644 --- a/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/utils.ts +++ b/blocksuite/affine/blocks/block-embed/src/embed-iframe-block/utils.ts @@ -1,3 +1,9 @@ +import { + EmbedIframeService, + FeatureFlagService, +} from '@blocksuite/affine-shared/services'; +import type { BlockStdScope } from '@blocksuite/block-std'; + /** * The options for the embed iframe url validation */ @@ -59,3 +65,16 @@ export function safeGetIframeSrc(htmlString: string): string | undefined { return undefined; } } + +/** + * Check if the url can be embedded as an iframe + * @param std The block std scope + * @param url The url to check + * @returns Whether the url can be embedded as an iframe + */ +export function canEmbedAsIframe(std: BlockStdScope, url: string) { + const featureFlag = std.get(FeatureFlagService); + const isEmbedIframeEnabled = featureFlag.getFlag('enable_embed_iframe_block'); + const embedIframeService = std.get(EmbedIframeService); + return isEmbedIframeEnabled && embedIframeService.canEmbed(url); +}