diff --git a/blocksuite/affine/block-embed/src/common/render-linked-doc.ts b/blocksuite/affine/block-embed/src/common/render-linked-doc.ts index e2e6878b8..7494df62b 100644 --- a/blocksuite/affine/block-embed/src/common/render-linked-doc.ts +++ b/blocksuite/affine/block-embed/src/common/render-linked-doc.ts @@ -10,7 +10,7 @@ import { import { EMBED_CARD_HEIGHT } from '@blocksuite/affine-shared/consts'; import { NotificationProvider } from '@blocksuite/affine-shared/services'; import { matchModels, SpecProvider } from '@blocksuite/affine-shared/utils'; -import { BlockStdScope } from '@blocksuite/block-std'; +import { BlockStdScope, EditorLifeCycleExtension } from '@blocksuite/block-std'; import { type BlockModel, type BlockSnapshot, @@ -351,7 +351,9 @@ export function notifyDocCreated(std: BlockStdScope, doc: Store) { // edit or undo or switch doc, close notify toast const addHandler = doc.history.on('stack-item-added', closeNotify); const popHandler = doc.history.on('stack-item-popped', closeNotify); - const disposable = std.host.slots.unmounted.on(closeNotify); + const disposable = std + .get(EditorLifeCycleExtension) + .slots.unmounted.on(closeNotify); notification.notify({ title: 'Linked doc created', diff --git a/blocksuite/affine/block-root/src/widgets/element-toolbar/change-note-button.ts b/blocksuite/affine/block-root/src/widgets/element-toolbar/change-note-button.ts index 14dc9003f..6f86073e8 100644 --- a/blocksuite/affine/block-root/src/widgets/element-toolbar/change-note-button.ts +++ b/blocksuite/affine/block-root/src/widgets/element-toolbar/change-note-button.ts @@ -30,6 +30,7 @@ import { TelemetryProvider, ThemeProvider, } from '@blocksuite/affine-shared/services'; +import { EditorLifeCycleExtension } from '@blocksuite/block-std'; import { Bound } from '@blocksuite/global/gfx'; import { WithDisposable } from '@blocksuite/global/lit'; import { @@ -210,7 +211,9 @@ export class EdgelessChangeNoteButton extends WithDisposable(LitElement) { const addHandler = this.doc.history.on('stack-item-added', closeNotify); const popHandler = this.doc.history.on('stack-item-popped', closeNotify); - const disposable = this.edgeless.std.host.slots.unmounted.on(closeNotify); + const disposable = this.edgeless.std + .get(EditorLifeCycleExtension) + .slots.unmounted.on(closeNotify); const undo = () => { this.doc.undo(); diff --git a/blocksuite/affine/components/src/embed-card-modal/embed-card-edit-modal.ts b/blocksuite/affine/components/src/embed-card-modal/embed-card-edit-modal.ts index f357f7467..fee0b942a 100644 --- a/blocksuite/affine/components/src/embed-card-modal/embed-card-edit-modal.ts +++ b/blocksuite/affine/components/src/embed-card-modal/embed-card-edit-modal.ts @@ -15,10 +15,11 @@ import { listenClickAway, stopPropagation, } from '@blocksuite/affine-shared/utils'; -import type { - BlockComponent, - BlockStdScope, - EditorHost, +import { + type BlockComponent, + type BlockStdScope, + type EditorHost, + EditorLifeCycleExtension, } from '@blocksuite/block-std'; import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit'; import { nextTick } from '@blocksuite/global/utils'; @@ -256,7 +257,9 @@ export class EmbedCardEditModal extends SignalWatcher( override connectedCallback() { super.connectedCallback(); - this.disposables.add(this.host.slots.unmounted.on(this._hide)); + this.disposables.add( + this.host.std.get(EditorLifeCycleExtension).slots.unmounted.on(this._hide) + ); this._updateInfo(); } diff --git a/blocksuite/affine/components/src/notification/linked-doc.ts b/blocksuite/affine/components/src/notification/linked-doc.ts index 2498f3c0b..18f57a9fb 100644 --- a/blocksuite/affine/components/src/notification/linked-doc.ts +++ b/blocksuite/affine/components/src/notification/linked-doc.ts @@ -1,5 +1,8 @@ import { NotificationProvider } from '@blocksuite/affine-shared/services'; -import type { BlockStdScope } from '@blocksuite/block-std'; +import { + type BlockStdScope, + EditorLifeCycleExtension, +} from '@blocksuite/block-std'; import { toast } from '../toast/toast.js'; @@ -26,7 +29,9 @@ function notify(std: BlockStdScope, title: string, message: string) { // edit or undo or switch doc, close notify toast const addHandler = doc.history.on('stack-item-added', closeNotify); const popHandler = doc.history.on('stack-item-popped', closeNotify); - const disposable = host.slots.unmounted.on(closeNotify); + const disposable = host.std + .get(EditorLifeCycleExtension) + .slots.unmounted.on(closeNotify); notification.notify({ title, diff --git a/blocksuite/framework/block-std/src/extension/editor-life-cycle.ts b/blocksuite/framework/block-std/src/extension/editor-life-cycle.ts new file mode 100644 index 000000000..250d6305b --- /dev/null +++ b/blocksuite/framework/block-std/src/extension/editor-life-cycle.ts @@ -0,0 +1,48 @@ +import { DisposableGroup, Slot } from '@blocksuite/global/slot'; + +import type { BlockStdScope } from '../scope/block-std-scope'; +import { LifeCycleWatcher } from './lifecycle-watcher'; + +export class EditorLifeCycleExtension extends LifeCycleWatcher { + static override key = 'editor-life-cycle'; + + disposables = new DisposableGroup(); + + readonly slots = { + created: new Slot(), + mounted: new Slot(), + rendered: new Slot(), + unmounted: new Slot(), + }; + + constructor(override readonly std: BlockStdScope) { + super(std); + + this.disposables.add(this.slots.created); + this.disposables.add(this.slots.mounted); + this.disposables.add(this.slots.rendered); + this.disposables.add(this.slots.unmounted); + } + + override created() { + super.created(); + this.slots.created.emit(); + } + + override mounted() { + super.mounted(); + this.slots.mounted.emit(); + } + + override rendered() { + super.rendered(); + this.slots.rendered.emit(); + } + + override unmounted() { + super.unmounted(); + this.slots.unmounted.emit(); + + this.disposables.dispose(); + } +} diff --git a/blocksuite/framework/block-std/src/extension/index.ts b/blocksuite/framework/block-std/src/extension/index.ts index 204b6473c..1c2fe198e 100644 --- a/blocksuite/framework/block-std/src/extension/index.ts +++ b/blocksuite/framework/block-std/src/extension/index.ts @@ -1,6 +1,7 @@ export * from './block-view.js'; export * from './config.js'; export * from './dnd/index.js'; +export * from './editor-life-cycle.js'; export * from './flavour.js'; export * from './keymap.js'; export * from './lifecycle-watcher.js'; diff --git a/blocksuite/framework/block-std/src/scope/block-std-scope.ts b/blocksuite/framework/block-std/src/scope/block-std-scope.ts index 636ca2e40..130d5956b 100644 --- a/blocksuite/framework/block-std/src/scope/block-std-scope.ts +++ b/blocksuite/framework/block-std/src/scope/block-std-scope.ts @@ -10,6 +10,7 @@ import { Clipboard } from '../clipboard/index.js'; import { CommandManager } from '../command/index.js'; import { UIEventDispatcher } from '../event/index.js'; import { DndController } from '../extension/dnd/index.js'; +import { EditorLifeCycleExtension } from '../extension/editor-life-cycle.js'; import { GfxController } from '../gfx/controller.js'; import { GfxSelectionManager } from '../gfx/selection.js'; import { SurfaceMiddlewareExtension } from '../gfx/surface-middleware.js'; @@ -41,6 +42,7 @@ const internalExtensions = [ SurfaceMiddlewareExtension, ViewManager, DndController, + EditorLifeCycleExtension, ]; export class BlockStdScope { diff --git a/blocksuite/framework/block-std/src/view/element/lit-host.ts b/blocksuite/framework/block-std/src/view/element/lit-host.ts index 63a4dbe81..534195c24 100644 --- a/blocksuite/framework/block-std/src/view/element/lit-host.ts +++ b/blocksuite/framework/block-std/src/view/element/lit-host.ts @@ -4,7 +4,6 @@ import { handleError, } from '@blocksuite/global/exceptions'; import { SignalWatcher, WithDisposable } from '@blocksuite/global/lit'; -import { Slot } from '@blocksuite/global/slot'; import { type BlockModel, Store, @@ -90,10 +89,6 @@ export class EditorHost extends SignalWatcher( )}`; }; - readonly slots = { - unmounted: new Slot(), - }; - get command(): CommandManager { return this.std.command; } @@ -131,8 +126,6 @@ export class EditorHost extends SignalWatcher( override disconnectedCallback() { super.disconnectedCallback(); this.std.unmount(); - this.slots.unmounted.emit(); - this.slots.unmounted.dispose(); } override async getUpdateComplete(): Promise { diff --git a/packages/backend/server/src/schema.gql b/packages/backend/server/src/schema.gql index c9268e638..44e7b4e55 100644 --- a/packages/backend/server/src/schema.gql +++ b/packages/backend/server/src/schema.gql @@ -330,7 +330,7 @@ type EditorType { name: String! } -union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | DocUpdateBlockedDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidOauthCallbackCodeDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType +union ErrorDataUnion = AlreadyInSpaceDataType | BlobNotFoundDataType | CopilotContextFileNotSupportedDataType | CopilotDocNotFoundDataType | CopilotFailedToMatchContextDataType | CopilotFailedToModifyContextDataType | CopilotInvalidContextDataType | CopilotMessageNotFoundDataType | CopilotPromptNotFoundDataType | CopilotProviderSideErrorDataType | DocActionDeniedDataType | DocHistoryNotFoundDataType | DocNotFoundDataType | DocUpdateBlockedDataType | ExpectToGrantDocUserRolesDataType | ExpectToRevokeDocUserRolesDataType | ExpectToUpdateDocUserRoleDataType | GraphqlBadRequestDataType | InvalidEmailDataType | InvalidHistoryTimestampDataType | InvalidLicenseUpdateParamsDataType | InvalidOauthCallbackCodeDataType | InvalidPasswordLengthDataType | InvalidRuntimeConfigTypeDataType | MemberNotFoundInSpaceDataType | MissingOauthQueryParameterDataType | NotInSpaceDataType | QueryTooLongDataType | RuntimeConfigNotFoundDataType | SameSubscriptionRecurringDataType | SpaceAccessDeniedDataType | SpaceNotFoundDataType | SpaceOwnerNotFoundDataType | SpaceShouldHaveOnlyOneOwnerDataType | SubscriptionAlreadyExistsDataType | SubscriptionNotExistsDataType | SubscriptionPlanNotFoundDataType | UnknownOauthProviderDataType | UnsupportedClientVersionDataType | UnsupportedSubscriptionPlanDataType | ValidationErrorDataType | VersionRejectedDataType | WorkspaceMembersExceedLimitToDowngradeDataType | WorkspacePermissionNotFoundDataType | WrongSignInCredentialsDataType enum ErrorNames { ACCESS_DENIED @@ -1231,6 +1231,10 @@ type UserType { token: tokenType! @deprecated(reason: "use [/api/auth/sign-in?native=true] instead") } +type ValidationErrorDataType { + errors: String! +} + type VersionRejectedDataType { serverVersion: String! version: String!