From 2c79d7229f0ef5b0537c8e374d025155b769050a Mon Sep 17 00:00:00 2001 From: Saul-Mirone Date: Wed, 26 Feb 2025 15:15:45 +0000 Subject: [PATCH] refactor(editor): remove legacy service watcher (#10455) The main changes in this PR involve replacing the deprecated `BlockServiceWatcher` with the new `LifeCycleWatcher` across multiple files. Here's a detailed breakdown: 1. **Core Architectural Change:** - Removed `BlockServiceWatcher` class completely (deleted file) - Migrated to `LifeCycleWatcher` as the new standard for watching component lifecycle events 2. **Key Changes in Implementation:** - Changed from using `blockService.specSlots` events to using `view.viewUpdated` events - Replaced `flavour` static property with `key` static property - Updated event handling to use more specific payload type checking 3. **Major File Changes:** - Modified multiple block components: - Embed synced doc block - Frame preview - Edgeless root spec - AI-related components (code, image, paragraph, etc.) - Quick search service - Edgeless clipboard 4. **Pattern of Changes:** The migration follows a consistent pattern: ```typescript // Old pattern class SomeWatcher extends BlockServiceWatcher { static override readonly flavour = 'some:flavour'; mounted() { this.blockService.specSlots.viewConnected.on(...) } } // New pattern class SomeWatcher extends LifeCycleWatcher { static override key = 'some-watcher'; mounted() { const { view } = this.std; view.viewUpdated.on(payload => { if (payload.type !== 'block' || payload.method !== 'add') return; // Handle event }); } } ``` 5. **Benefits:** - More explicit and type-safe event handling - Cleaner architecture by removing deprecated code - More consistent approach to lifecycle management - Better separation of concerns This appears to be a significant architectural improvement that modernizes the codebase by removing deprecated patterns and standardizing on a more robust lifecycle management system. --- .../embed-synced-doc-block.ts | 36 +++--- .../components/frame/frame-preview.ts | 34 +++--- .../src/edgeless/edgeless-root-spec.ts | 20 ++-- .../block-std/src/extension/index.ts | 1 - .../src/extension/service-watcher.ts | 47 -------- .../src/blocksuite/ai/extensions/ai-code.ts | 18 +-- .../ai/extensions/ai-edgeless-root.ts | 37 +++--- .../src/blocksuite/ai/extensions/ai-image.ts | 17 ++- .../blocksuite/ai/extensions/ai-page-root.ts | 29 +++-- .../blocksuite/ai/extensions/ai-paragraph.ts | 10 +- .../extensions/edgeless-clipboard.ts | 73 ++++++------ .../extensions/quick-search-service.ts | 108 ++++++++---------- .../editor/edgeless/snapshot.tsx | 23 ++-- 13 files changed, 208 insertions(+), 245 deletions(-) delete mode 100644 blocksuite/framework/block-std/src/extension/service-watcher.ts diff --git a/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-synced-doc-block.ts b/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-synced-doc-block.ts index 0a1588ba84..2737312812 100644 --- a/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-synced-doc-block.ts +++ b/blocksuite/affine/block-embed/src/embed-synced-doc-block/embed-synced-doc-block.ts @@ -26,9 +26,9 @@ import { } from '@blocksuite/affine-shared/utils'; import { BlockSelection, - BlockServiceWatcher, BlockStdScope, type EditorHost, + LifeCycleWatcher, } from '@blocksuite/block-std'; import { GfxControllerIdentifier, @@ -124,27 +124,31 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent { - const nextComponent = component as EmbedSyncedDocBlockComponent; + override mounted(): void { + const { view } = this.std; + view.viewUpdated.on(payload => { + if ( + payload.type !== 'block' || + payload.view.model.flavour !== 'affine:embed-synced-doc' + ) { + return; + } + const nextComponent = payload.view as EmbedSyncedDocBlockComponent; + if (payload.method === 'add') { nextComponent.depth = nextDepth; currentDisposables.add(() => { nextComponent.depth = 0; }); - }) - ); - disposableGroup.add( - slots.viewDisconnected.on(({ component }) => { - const nextComponent = component as EmbedSyncedDocBlockComponent; + return; + } + if (payload.method === 'delete') { nextComponent.depth = 0; - }) - ); + return; + } + }); } } diff --git a/blocksuite/affine/block-root/src/edgeless/components/frame/frame-preview.ts b/blocksuite/affine/block-root/src/edgeless/components/frame/frame-preview.ts index 924f5767e6..ba771cf798 100644 --- a/blocksuite/affine/block-root/src/edgeless/components/frame/frame-preview.ts +++ b/blocksuite/affine/block-root/src/edgeless/components/frame/frame-preview.ts @@ -1,9 +1,9 @@ import type { FrameBlockModel } from '@blocksuite/affine-model'; import { SpecProvider } from '@blocksuite/affine-shared/utils'; import { - BlockServiceWatcher, BlockStdScope, type EditorHost, + LifeCycleWatcher, ShadowlessElement, } from '@blocksuite/block-std'; import { GfxControllerIdentifier } from '@blocksuite/block-std/gfx'; @@ -117,22 +117,26 @@ export class FramePreview extends WithDisposable(ShadowlessElement) { private _initSpec() { const refreshViewport = this._refreshViewport.bind(this); - class FramePreviewWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:page'; + class FramePreviewWatcher extends LifeCycleWatcher { + static override key = 'frame-preview-watcher'; override mounted() { - const blockService = this.blockService; - blockService.disposables.add( - blockService.specSlots.viewConnected.on(({ component }) => { - const edgelessBlock = - component as EdgelessRootPreviewBlockComponent; - - edgelessBlock.editorViewportSelector = 'frame-preview-viewport'; - edgelessBlock.service.viewport.sizeUpdated.once(() => { - refreshViewport(); - }); - }) - ); + const { view } = this.std; + view.viewUpdated.on(payload => { + if ( + payload.type !== 'block' || + payload.method !== 'add' || + payload.view.model.flavour !== 'affine:page' + ) { + return; + } + const edgelessBlock = + payload.view as EdgelessRootPreviewBlockComponent; + edgelessBlock.editorViewportSelector = 'frame-preview-viewport'; + edgelessBlock.service.viewport.sizeUpdated.once(() => { + refreshViewport(); + }); + }); } } this._previewSpec.extend([FramePreviewWatcher]); diff --git a/blocksuite/affine/block-root/src/edgeless/edgeless-root-spec.ts b/blocksuite/affine/block-root/src/edgeless/edgeless-root-spec.ts index 3e7c5607ff..47adb4b19a 100644 --- a/blocksuite/affine/block-root/src/edgeless/edgeless-root-spec.ts +++ b/blocksuite/affine/block-root/src/edgeless/edgeless-root-spec.ts @@ -2,11 +2,14 @@ import { autoConnectWidget } from '@blocksuite/affine-widget-edgeless-auto-conne import { frameTitleWidget } from '@blocksuite/affine-widget-frame-title'; import { edgelessRemoteSelectionWidget } from '@blocksuite/affine-widget-remote-selection'; import { - BlockServiceWatcher, BlockViewExtension, + LifeCycleWatcher, WidgetViewExtension, } from '@blocksuite/block-std'; -import { ToolController } from '@blocksuite/block-std/gfx'; +import { + GfxControllerIdentifier, + ToolController, +} from '@blocksuite/block-std/gfx'; import type { ExtensionType } from '@blocksuite/store'; import { literal, unsafeStatic } from 'lit/static-html.js'; @@ -56,17 +59,12 @@ export const edgelessToolbarWidget = WidgetViewExtension( literal`${unsafeStatic(EDGELESS_TOOLBAR_WIDGET)}` ); -class EdgelessLocker extends BlockServiceWatcher { - static override readonly flavour = 'affine:page'; +class EdgelessLocker extends LifeCycleWatcher { + static override key = 'edgeless-locker'; override mounted() { - const service = this.blockService; - service.disposables.add( - service.specSlots.viewConnected.on(({ service }) => { - // Does not allow the user to move and zoom. - (service as EdgelessRootService).locked = true; - }) - ); + const { viewport } = this.std.get(GfxControllerIdentifier); + viewport.locked = true; } } diff --git a/blocksuite/framework/block-std/src/extension/index.ts b/blocksuite/framework/block-std/src/extension/index.ts index 41d8534147..204b6473cd 100644 --- a/blocksuite/framework/block-std/src/extension/index.ts +++ b/blocksuite/framework/block-std/src/extension/index.ts @@ -5,5 +5,4 @@ export * from './flavour.js'; export * from './keymap.js'; export * from './lifecycle-watcher.js'; export * from './service.js'; -export * from './service-watcher.js'; export * from './widget-view-map.js'; diff --git a/blocksuite/framework/block-std/src/extension/service-watcher.ts b/blocksuite/framework/block-std/src/extension/service-watcher.ts deleted file mode 100644 index 6804e2e3fe..0000000000 --- a/blocksuite/framework/block-std/src/extension/service-watcher.ts +++ /dev/null @@ -1,47 +0,0 @@ -import type { Container } from '@blocksuite/global/di'; -import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions'; - -import { - BlockServiceIdentifier, - LifeCycleWatcherIdentifier, - StdIdentifier, -} from '../identifier.js'; -import type { BlockStdScope } from '../scope/index.js'; -import { LifeCycleWatcher } from './lifecycle-watcher.js'; -import type { BlockService } from './service.js'; - -const idMap = new Map(); - -/** - * @deprecated - * BlockServiceWatcher is deprecated. You should reconsider where to put your feature. - * - * BlockServiceWatcher is a legacy extension that is used to watch the slots registered on block service. - * However, we recommend using the new extension system. - */ -export abstract class BlockServiceWatcher extends LifeCycleWatcher { - static flavour: string; - - constructor( - std: BlockStdScope, - readonly blockService: BlockService - ) { - super(std); - } - - static override setup(di: Container) { - if (!this.flavour) { - throw new BlockSuiteError( - ErrorCode.ValueNotExists, - 'Flavour is not defined in the BlockServiceWatcher' - ); - } - const id = idMap.get(this.flavour) ?? 0; - idMap.set(this.flavour, id + 1); - di.addImpl( - LifeCycleWatcherIdentifier(`${this.flavour}-watcher-${id}`), - this, - [StdIdentifier, BlockServiceIdentifier(this.flavour)] - ); - } -} diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts index 188c9dd101..254ccf2f9a 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-code.ts @@ -1,4 +1,4 @@ -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { AffineCodeToolbarWidget, CodeBlockSpec, @@ -7,15 +7,19 @@ import type { ExtensionType } from '@blocksuite/affine/store'; import { setupCodeToolbarAIEntry } from '../entries/code-toolbar/setup-code-toolbar'; -class AICodeBlockWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:code'; +class AICodeBlockWatcher extends LifeCycleWatcher { + static override key = 'ai-code-block-watcher'; override mounted() { super.mounted(); - const service = this.blockService; - service.specSlots.widgetConnected.on(view => { - if (view.component instanceof AffineCodeToolbarWidget) { - setupCodeToolbarAIEntry(view.component); + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'widget' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (component instanceof AffineCodeToolbarWidget) { + setupCodeToolbarAIEntry(component); } }); } diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts index 9dafec75d4..cde2d31f0c 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-edgeless-root.ts @@ -1,4 +1,4 @@ -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { AffineFormatBarWidget, AffineSlashMenuWidget, @@ -39,32 +39,37 @@ export function createAIEdgelessRootBlockSpec( } function getAIEdgelessRootWatcher(framework: FrameworkProvider) { - class AIEdgelessRootWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:page'; + class AIEdgelessRootWatcher extends LifeCycleWatcher { + static override key = 'ai-edgeless-root-watcher'; override mounted() { super.mounted(); - this.blockService.specSlots.widgetConnected.on(view => { - if (view.component instanceof AffineAIPanelWidget) { - view.component.style.width = '430px'; - view.component.config = buildAIPanelConfig(view.component, framework); - setupSpaceAIEntry(view.component); + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'widget' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (component instanceof AffineAIPanelWidget) { + component.style.width = '430px'; + component.config = buildAIPanelConfig(component, framework); + setupSpaceAIEntry(component); } - if (view.component instanceof EdgelessCopilotWidget) { - setupEdgelessCopilot(view.component); + if (component instanceof EdgelessCopilotWidget) { + setupEdgelessCopilot(component); } - if (view.component instanceof EdgelessElementToolbarWidget) { - setupEdgelessElementToolbarAIEntry(view.component); + if (component instanceof EdgelessElementToolbarWidget) { + setupEdgelessElementToolbarAIEntry(component); } - if (view.component instanceof AffineFormatBarWidget) { - setupFormatBarAIEntry(view.component); + if (component instanceof AffineFormatBarWidget) { + setupFormatBarAIEntry(component); } - if (view.component instanceof AffineSlashMenuWidget) { - setupSlashMenuAIEntry(view.component); + if (component instanceof AffineSlashMenuWidget) { + setupSlashMenuAIEntry(component); } }); } diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-image.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-image.ts index 9d887e4b16..11722fcd12 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-image.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-image.ts @@ -1,4 +1,4 @@ -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { AffineImageToolbarWidget, ImageBlockSpec, @@ -7,14 +7,19 @@ import type { ExtensionType } from '@blocksuite/affine/store'; import { setupImageToolbarAIEntry } from '../entries/image-toolbar/setup-image-toolbar'; -class AIImageBlockWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:image'; +class AIImageBlockWatcher extends LifeCycleWatcher { + static override key = 'ai-image-block-watcher'; override mounted() { super.mounted(); - this.blockService.specSlots.widgetConnected.on(view => { - if (view.component instanceof AffineImageToolbarWidget) { - setupImageToolbarAIEntry(view.component); + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'widget' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (component instanceof AffineImageToolbarWidget) { + setupImageToolbarAIEntry(component); } }); } diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts index da8f40a24e..c8ec4f7fb1 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-page-root.ts @@ -1,4 +1,4 @@ -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { AffineFormatBarWidget, AffineSlashMenuWidget, @@ -17,24 +17,29 @@ import { } from '../widgets/ai-panel/ai-panel'; function getAIPageRootWatcher(framework: FrameworkProvider) { - class AIPageRootWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:page'; + class AIPageRootWatcher extends LifeCycleWatcher { + static override key = 'ai-page-root-watcher'; override mounted() { super.mounted(); - this.blockService.specSlots.widgetConnected.on(view => { - if (view.component instanceof AffineAIPanelWidget) { - view.component.style.width = '630px'; - view.component.config = buildAIPanelConfig(view.component, framework); - setupSpaceAIEntry(view.component); + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'widget' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (component instanceof AffineAIPanelWidget) { + component.style.width = '630px'; + component.config = buildAIPanelConfig(component, framework); + setupSpaceAIEntry(component); } - if (view.component instanceof AffineFormatBarWidget) { - setupFormatBarAIEntry(view.component); + if (component instanceof AffineFormatBarWidget) { + setupFormatBarAIEntry(component); } - if (view.component instanceof AffineSlashMenuWidget) { - setupSlashMenuAIEntry(view.component); + if (component instanceof AffineSlashMenuWidget) { + setupSlashMenuAIEntry(component); } }); } diff --git a/packages/frontend/core/src/blocksuite/ai/extensions/ai-paragraph.ts b/packages/frontend/core/src/blocksuite/ai/extensions/ai-paragraph.ts index 58ed949900..adfeb8260c 100644 --- a/packages/frontend/core/src/blocksuite/ai/extensions/ai-paragraph.ts +++ b/packages/frontend/core/src/blocksuite/ai/extensions/ai-paragraph.ts @@ -1,18 +1,16 @@ -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { ParagraphBlockService, ParagraphBlockSpec, } from '@blocksuite/affine/blocks'; -import { assertInstanceOf } from '@blocksuite/affine/global/utils'; import type { ExtensionType } from '@blocksuite/affine/store'; -class AIParagraphBlockWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:paragraph'; +class AIParagraphBlockWatcher extends LifeCycleWatcher { + static override key = 'ai-paragraph-block-watcher'; override mounted() { super.mounted(); - const service = this.blockService; - assertInstanceOf(service, ParagraphBlockService); + const service = this.std.get(ParagraphBlockService); service.placeholderGenerator = model => { if (model.type === 'text') { return "Type '/' for commands, 'space' for AI"; diff --git a/packages/frontend/core/src/blocksuite/extensions/edgeless-clipboard.ts b/packages/frontend/core/src/blocksuite/extensions/edgeless-clipboard.ts index ee6b042e55..7f1cc64b40 100644 --- a/packages/frontend/core/src/blocksuite/extensions/edgeless-clipboard.ts +++ b/packages/frontend/core/src/blocksuite/extensions/edgeless-clipboard.ts @@ -1,47 +1,44 @@ import { AIChatBlockSchema } from '@affine/core/blocksuite/ai/blocks'; -import { BlockServiceWatcher } from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import { EdgelessRootBlockComponent } from '@blocksuite/affine/blocks'; import type { BlockSnapshot } from '@blocksuite/affine/store'; -export class EdgelessClipboardWatcher extends BlockServiceWatcher { - static override readonly flavour = 'affine:page'; +export class EdgelessClipboardWatcher extends LifeCycleWatcher { + static override key = 'edgeless-clipboard-watcher'; override mounted() { super.mounted(); - this.blockService.disposables.add( - this.blockService.specSlots.viewConnected.on(view => { - const { component } = view; - if (component instanceof EdgelessRootBlockComponent) { - const AIChatBlockFlavour = AIChatBlockSchema.model.flavour; - const createFunc = (block: BlockSnapshot) => { - const { - xywh, - scale, - messages, - sessionId, - rootDocId, - rootWorkspaceId, - } = block.props; - const blockId = component.service.crud.addBlock( - AIChatBlockFlavour, - { - xywh, - scale, - messages, - sessionId, - rootDocId, - rootWorkspaceId, - }, - component.surface.model.id - ); - return blockId; - }; - component.clipboardController.registerBlock( - AIChatBlockFlavour, - createFunc - ); - } - }) - ); + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'block' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (!(component instanceof EdgelessRootBlockComponent)) { + return; + } + const AIChatBlockFlavour = AIChatBlockSchema.model.flavour; + const createFunc = (block: BlockSnapshot) => { + const { xywh, scale, messages, sessionId, rootDocId, rootWorkspaceId } = + block.props; + const blockId = component.service.crud.addBlock( + AIChatBlockFlavour, + { + xywh, + scale, + messages, + sessionId, + rootDocId, + rootWorkspaceId, + }, + component.surface.model.id + ); + return blockId; + }; + component.clipboardController.registerBlock( + AIChatBlockFlavour, + createFunc + ); + }); } } diff --git a/packages/frontend/core/src/blocksuite/extensions/quick-search-service.ts b/packages/frontend/core/src/blocksuite/extensions/quick-search-service.ts index 5fbb5612fc..2bd93d541d 100644 --- a/packages/frontend/core/src/blocksuite/extensions/quick-search-service.ts +++ b/packages/frontend/core/src/blocksuite/extensions/quick-search-service.ts @@ -10,10 +10,7 @@ import { import { ExternalLinksQuickSearchSession } from '@affine/core/modules/quicksearch/impls/external-links'; import { JournalsQuickSearchSession } from '@affine/core/modules/quicksearch/impls/journals'; import { track } from '@affine/track'; -import { - BlockServiceWatcher, - type WidgetComponent, -} from '@blocksuite/affine/block-std'; +import { LifeCycleWatcher } from '@blocksuite/affine/block-std'; import type { QuickSearchResult } from '@blocksuite/affine/blocks'; import { AffineSlashMenuWidget, @@ -123,65 +120,56 @@ export function patchQuickSearchService(framework: FrameworkProvider) { }, }); - const SlashMenuQuickSearchExtension = patchSpecService( - 'affine:page', - (component: WidgetComponent) => { - if (component instanceof AffineSlashMenuWidget) { - component.config.items.forEach(item => { - if ( - 'action' in item && - (item.name === 'Linked Doc' || item.name === 'Link') - ) { - item.action = async ({ rootComponent }) => { - const [success, { insertedLinkType }] = - rootComponent.std.command.exec(insertLinkByQuickSearchCommand); + class SlashMenuQuickSearchExtension extends LifeCycleWatcher { + static override key = 'slash-menu-quick-search-extension'; - if (!success) return; - - insertedLinkType - ?.then(type => { - const flavour = type?.flavour; - if (!flavour) return; - - if (flavour === 'affine:bookmark') { - track.doc.editor.slashMenu.bookmark(); - return; - } - - if (flavour === 'affine:embed-linked-doc') { - track.doc.editor.slashMenu.linkDoc({ - control: 'linkDoc', - }); - return; - } - }) - .catch(console.error); - }; - } - }); - } - } - ); - return [QuickSearch, SlashMenuQuickSearchExtension]; -} - -function patchSpecService( - flavour: string, - onWidgetConnected?: (component: WidgetComponent) => void -) { - class TempServiceWatcher extends BlockServiceWatcher { - static override readonly flavour = flavour; override mounted() { super.mounted(); - const disposableGroup = this.blockService.disposables; - if (onWidgetConnected) { - disposableGroup.add( - this.blockService.specSlots.widgetConnected.on(({ component }) => { - onWidgetConnected(component); - }) - ); - } + const { view } = this.std; + view.viewUpdated.on(payload => { + if (payload.type !== 'widget' || payload.method !== 'add') { + return; + } + const component = payload.view; + if (component instanceof AffineSlashMenuWidget) { + component.config.items.forEach(item => { + if ( + 'action' in item && + (item.name === 'Linked Doc' || item.name === 'Link') + ) { + item.action = async ({ rootComponent }) => { + const [success, { insertedLinkType }] = + rootComponent.std.command.exec( + insertLinkByQuickSearchCommand + ); + + if (!success) return; + + insertedLinkType + ?.then(type => { + const flavour = type?.flavour; + if (!flavour) return; + + if (flavour === 'affine:bookmark') { + track.doc.editor.slashMenu.bookmark(); + return; + } + + if (flavour === 'affine:embed-linked-doc') { + track.doc.editor.slashMenu.linkDoc({ + control: 'linkDoc', + }); + return; + } + }) + .catch(console.error); + }; + } + }); + } + }); } } - return TempServiceWatcher; + + return [QuickSearch, SlashMenuQuickSearchExtension]; } diff --git a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx index afbc544445..f8e9cdfbdb 100644 --- a/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx +++ b/packages/frontend/core/src/desktop/dialogs/setting/general-setting/editor/edgeless/snapshot.tsx @@ -2,16 +2,14 @@ import { Skeleton } from '@affine/component'; import type { EditorSettingSchema } from '@affine/core/modules/editor-setting'; import { EditorSettingService } from '@affine/core/modules/editor-setting'; import type { EditorHost } from '@blocksuite/affine/block-std'; -import { - BlockServiceIdentifier, - BlockStdScope, -} from '@blocksuite/affine/block-std'; +import { BlockStdScope } from '@blocksuite/affine/block-std'; import { GfxControllerIdentifier, type GfxPrimitiveElementModel, } from '@blocksuite/affine/block-std/gfx'; import { EdgelessCRUDIdentifier, + type EdgelessRootPreviewBlockComponent, SpecProvider, } from '@blocksuite/affine/blocks'; import { Bound } from '@blocksuite/affine/global/utils'; @@ -93,14 +91,18 @@ export const EdgelessSnapshot = (props: Props) => { } // refresh viewport - const edgelessService = editorHost.std.get( - BlockServiceIdentifier('affine:page') - ); const gfx = editorHost.std.get(GfxControllerIdentifier); - edgelessService.specSlots.viewConnected.once(({ component }) => { - const edgelessBlock = component as any; + const disposable = editorHost.std.view.viewUpdated.on(payload => { + if ( + payload.type !== 'block' || + payload.method !== 'add' || + payload.view.model.flavour !== 'affine:page' + ) { + return; + } + const component = payload.view as EdgelessRootPreviewBlockComponent; doc.readonly = false; - edgelessBlock.editorViewportSelector = 'ref-viewport'; + component.editorViewportSelector = 'ref-viewport'; const frame = getFrameBlock(doc); if (frame && docName !== 'frame') { // docName with value 'frame' shouldn't be deleted, it is a part of frame settings @@ -110,6 +112,7 @@ export const EdgelessSnapshot = (props: Props) => { const bound = boundMap.get(docName); bound && gfx.viewport.setViewportByBound(bound); doc.readonly = true; + disposable.dispose(); }); // append to dom node