mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-29 16:19:43 +08:00
fix(editor): cleanup transformer middleware slot subscriptions (#12630)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Improved resource management by introducing explicit cleanup for various middleware components, ensuring that resources are properly released when no longer needed. - **Refactor** - Updated middleware logic to support cleanup functions, enhancing the stability and performance of the application by preventing potential memory leaks. - **Chores** - Enhanced lifecycle management in core systems to automatically dispose of resources when appropriate. <!-- end of auto-generated comment: release notes by coderabbit.ai -->
This commit is contained in:
@@ -37,7 +37,7 @@ const handlePoint = (
|
|||||||
};
|
};
|
||||||
|
|
||||||
const sliceText = (slots: TransformerSlots, std: EditorHost['std']) => {
|
const sliceText = (slots: TransformerSlots, std: EditorHost['std']) => {
|
||||||
slots.afterExport.subscribe(payload => {
|
const afterExportSubscription = slots.afterExport.subscribe(payload => {
|
||||||
if (payload.type === 'block') {
|
if (payload.type === 'block') {
|
||||||
const snapshot = payload.snapshot;
|
const snapshot = payload.snapshot;
|
||||||
|
|
||||||
@@ -53,10 +53,14 @@ const sliceText = (slots: TransformerSlots, std: EditorHost['std']) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
afterExportSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export const copyMiddleware = (std: BlockStdScope): TransformerMiddleware => {
|
export const copyMiddleware = (std: BlockStdScope): TransformerMiddleware => {
|
||||||
return ({ slots }) => {
|
return ({ slots }) => {
|
||||||
sliceText(slots, std);
|
return sliceText(slots, std);
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ import type { TransformerMiddleware } from '@blocksuite/store';
|
|||||||
export const fileNameMiddleware =
|
export const fileNameMiddleware =
|
||||||
(fileName?: string): TransformerMiddleware =>
|
(fileName?: string): TransformerMiddleware =>
|
||||||
({ slots }) => {
|
({ slots }) => {
|
||||||
slots.beforeImport.subscribe(payload => {
|
const beforeImportSubscription = slots.beforeImport.subscribe(payload => {
|
||||||
if (payload.type !== 'page') {
|
if (payload.type !== 'page') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -20,4 +20,8 @@ export const fileNameMiddleware =
|
|||||||
],
|
],
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
beforeImportSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -528,7 +528,7 @@ export const pasteMiddleware = (
|
|||||||
): TransformerMiddleware => {
|
): TransformerMiddleware => {
|
||||||
return ({ slots }) => {
|
return ({ slots }) => {
|
||||||
let tr: PasteTr | undefined;
|
let tr: PasteTr | undefined;
|
||||||
slots.beforeImport.subscribe(payload => {
|
const beforeImportSubscription = slots.beforeImport.subscribe(payload => {
|
||||||
if (payload.type === 'slice') {
|
if (payload.type === 'slice') {
|
||||||
const { snapshot } = payload;
|
const { snapshot } = payload;
|
||||||
flatNote(snapshot);
|
flatNote(snapshot);
|
||||||
@@ -543,13 +543,18 @@ export const pasteMiddleware = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
slots.afterImport.subscribe(payload => {
|
const afterImportSubscription = slots.afterImport.subscribe(payload => {
|
||||||
if (tr && payload.type === 'slice') {
|
if (tr && payload.type === 'slice') {
|
||||||
tr.pasted();
|
tr.pasted();
|
||||||
tr.focusPasted();
|
tr.focusPasted();
|
||||||
tr.convertToLinkedDoc();
|
tr.convertToLinkedDoc();
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
beforeImportSubscription.unsubscribe();
|
||||||
|
afterImportSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ export const replaceIdMiddleware =
|
|||||||
map(({ model }) => model)
|
map(({ model }) => model)
|
||||||
);
|
);
|
||||||
|
|
||||||
afterImportBlock$
|
const afterImportBlockSubscription = afterImportBlock$
|
||||||
.pipe(filter(model => matchModels(model, [DatabaseBlockModel])))
|
.pipe(filter(model => matchModels(model, [DatabaseBlockModel])))
|
||||||
.subscribe(model => {
|
.subscribe(model => {
|
||||||
Object.keys(model.props.cells).forEach(cellId => {
|
Object.keys(model.props.cells).forEach(cellId => {
|
||||||
@@ -44,7 +44,7 @@ export const replaceIdMiddleware =
|
|||||||
});
|
});
|
||||||
|
|
||||||
// replace LinkedPage pageId with new id in paragraph blocks
|
// replace LinkedPage pageId with new id in paragraph blocks
|
||||||
afterImportBlock$
|
const replaceLinkedPageIdSubscription = afterImportBlock$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(model =>
|
filter(model =>
|
||||||
matchModels(model, [ParagraphBlockModel, ListBlockModel])
|
matchModels(model, [ParagraphBlockModel, ListBlockModel])
|
||||||
@@ -84,7 +84,7 @@ export const replaceIdMiddleware =
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
afterImportBlock$
|
const replaceSurfaceRefIdSubscription = afterImportBlock$
|
||||||
.pipe(filter(model => matchModels(model, [SurfaceRefBlockModel])))
|
.pipe(filter(model => matchModels(model, [SurfaceRefBlockModel])))
|
||||||
.subscribe(model => {
|
.subscribe(model => {
|
||||||
const original = model.props.reference;
|
const original = model.props.reference;
|
||||||
@@ -105,7 +105,7 @@ export const replaceIdMiddleware =
|
|||||||
});
|
});
|
||||||
|
|
||||||
// TODO(@fundon): process linked block/element
|
// TODO(@fundon): process linked block/element
|
||||||
afterImportBlock$
|
const replaceLinkedDocIdSubscription = afterImportBlock$
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(model =>
|
filter(model =>
|
||||||
matchModels(model, [EmbedLinkedDocModel, EmbedSyncedDocModel])
|
matchModels(model, [EmbedLinkedDocModel, EmbedSyncedDocModel])
|
||||||
@@ -128,7 +128,7 @@ export const replaceIdMiddleware =
|
|||||||
|
|
||||||
// Before Import
|
// Before Import
|
||||||
|
|
||||||
slots.beforeImport
|
const beforeImportPageSubscription = slots.beforeImport
|
||||||
.pipe(filter(payload => payload.type === 'page'))
|
.pipe(filter(payload => payload.type === 'page'))
|
||||||
.subscribe(payload => {
|
.subscribe(payload => {
|
||||||
if (idMap.has(payload.snapshot.meta.id)) {
|
if (idMap.has(payload.snapshot.meta.id)) {
|
||||||
@@ -140,7 +140,7 @@ export const replaceIdMiddleware =
|
|||||||
payload.snapshot.meta.id = newId;
|
payload.snapshot.meta.id = newId;
|
||||||
});
|
});
|
||||||
|
|
||||||
slots.beforeImport
|
const beforeImportBlockSubscription = slots.beforeImport
|
||||||
.pipe(
|
.pipe(
|
||||||
filter(
|
filter(
|
||||||
(payload): payload is BeforeImportBlockPayload =>
|
(payload): payload is BeforeImportBlockPayload =>
|
||||||
@@ -244,4 +244,13 @@ export const replaceIdMiddleware =
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
afterImportBlockSubscription.unsubscribe();
|
||||||
|
replaceLinkedPageIdSubscription.unsubscribe();
|
||||||
|
replaceSurfaceRefIdSubscription.unsubscribe();
|
||||||
|
replaceLinkedDocIdSubscription.unsubscribe();
|
||||||
|
beforeImportPageSubscription.unsubscribe();
|
||||||
|
beforeImportBlockSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -5,33 +5,42 @@ export const surfaceRefToEmbed =
|
|||||||
(std: BlockStdScope): TransformerMiddleware =>
|
(std: BlockStdScope): TransformerMiddleware =>
|
||||||
({ slots }) => {
|
({ slots }) => {
|
||||||
let pageId: string | null = null;
|
let pageId: string | null = null;
|
||||||
slots.beforeImport.subscribe(payload => {
|
const beforeImportSliceSubscription = slots.beforeImport.subscribe(
|
||||||
if (payload.type === 'slice') {
|
payload => {
|
||||||
pageId = payload.snapshot.pageId;
|
if (payload.type === 'slice') {
|
||||||
|
pageId = payload.snapshot.pageId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
slots.beforeImport.subscribe(payload => {
|
const beforeImportBlockSubscription = slots.beforeImport.subscribe(
|
||||||
// only handle surface-ref block snapshot
|
payload => {
|
||||||
if (
|
// only handle surface-ref block snapshot
|
||||||
payload.type !== 'block' ||
|
if (
|
||||||
payload.snapshot.flavour !== 'affine:surface-ref'
|
payload.type !== 'block' ||
|
||||||
)
|
payload.snapshot.flavour !== 'affine:surface-ref'
|
||||||
return;
|
)
|
||||||
|
return;
|
||||||
|
|
||||||
// turn into embed-linked-doc if the current doc is different from the pageId of the surface-ref block
|
// turn into embed-linked-doc if the current doc is different from the pageId of the surface-ref block
|
||||||
const isNotSameDoc = pageId !== std.store.doc.id;
|
const isNotSameDoc = pageId !== std.store.doc.id;
|
||||||
if (pageId && isNotSameDoc) {
|
if (pageId && isNotSameDoc) {
|
||||||
// The blockId of the original surface-ref block
|
// The blockId of the original surface-ref block
|
||||||
const blockId = payload.snapshot.id;
|
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 = {
|
||||||
pageId,
|
pageId,
|
||||||
params: {
|
params: {
|
||||||
mode: 'page',
|
mode: 'page',
|
||||||
blockIds: [blockId],
|
blockIds: [blockId],
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
beforeImportSliceSubscription.unsubscribe();
|
||||||
|
beforeImportBlockSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -3,9 +3,13 @@ import type { DocMeta, TransformerMiddleware } from '@blocksuite/store';
|
|||||||
export const titleMiddleware =
|
export const titleMiddleware =
|
||||||
(metas: DocMeta[]): TransformerMiddleware =>
|
(metas: DocMeta[]): TransformerMiddleware =>
|
||||||
({ slots, adapterConfigs }) => {
|
({ slots, adapterConfigs }) => {
|
||||||
slots.beforeExport.subscribe(() => {
|
const beforeExportSubscription = slots.beforeExport.subscribe(() => {
|
||||||
for (const meta of metas) {
|
for (const meta of metas) {
|
||||||
adapterConfigs.set('title:' + meta.id, meta.title);
|
adapterConfigs.set('title:' + meta.id, meta.title);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
beforeExportSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ export const uploadMiddleware = (
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
blockView$
|
const blockViewSubscription = blockView$
|
||||||
.pipe(
|
.pipe(
|
||||||
map(payload => {
|
map(payload => {
|
||||||
if (assetsManager.uploadingAssetsMap.size === 0) return null;
|
if (assetsManager.uploadingAssetsMap.size === 0) return null;
|
||||||
@@ -110,5 +110,9 @@ export const uploadMiddleware = (
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
.subscribe();
|
.subscribe();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
blockViewSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ export const gfxBlocksFilter = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return ({ slots, transformerConfigs }) => {
|
return ({ slots, transformerConfigs }) => {
|
||||||
slots.beforeExport.subscribe(payload => {
|
const beforeExportSubscription = slots.beforeExport.subscribe(payload => {
|
||||||
if (payload.type !== 'block') {
|
if (payload.type !== 'block') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -54,7 +54,7 @@ export const gfxBlocksFilter = (
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
slots.afterExport.subscribe(payload => {
|
const afterExportSubscription = slots.afterExport.subscribe(payload => {
|
||||||
if (payload.type !== 'block') {
|
if (payload.type !== 'block') {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -110,5 +110,10 @@ export const gfxBlocksFilter = (
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
beforeExportSubscription.unsubscribe();
|
||||||
|
afterExportSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,36 +9,45 @@ export const newIdCrossDoc =
|
|||||||
let samePage = false;
|
let samePage = false;
|
||||||
const oldToNewIdMap = new Map<string, string>();
|
const oldToNewIdMap = new Map<string, string>();
|
||||||
|
|
||||||
slots.beforeImport.subscribe(payload => {
|
const beforeImportSliceSubscription = slots.beforeImport.subscribe(
|
||||||
if (payload.type === 'slice') {
|
payload => {
|
||||||
samePage = payload.snapshot.pageId === std.store.id;
|
if (payload.type === 'slice') {
|
||||||
|
samePage = payload.snapshot.pageId === std.store.id;
|
||||||
|
}
|
||||||
|
if (payload.type === 'block' && !samePage) {
|
||||||
|
const newId = std.workspace.idGenerator();
|
||||||
|
|
||||||
|
oldToNewIdMap.set(payload.snapshot.id, newId);
|
||||||
|
payload.snapshot.id = newId;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (payload.type === 'block' && !samePage) {
|
);
|
||||||
const newId = std.workspace.idGenerator();
|
|
||||||
|
|
||||||
oldToNewIdMap.set(payload.snapshot.id, newId);
|
const afterImportBlockSubscription = slots.afterImport.subscribe(
|
||||||
payload.snapshot.id = newId;
|
payload => {
|
||||||
|
if (
|
||||||
|
!samePage &&
|
||||||
|
payload.type === 'block' &&
|
||||||
|
matchModels(payload.model, [DatabaseBlockModel])
|
||||||
|
) {
|
||||||
|
const originalCells = payload.model.props.cells;
|
||||||
|
const newCells = {
|
||||||
|
...originalCells,
|
||||||
|
};
|
||||||
|
|
||||||
|
Object.keys(originalCells).forEach(cellId => {
|
||||||
|
if (oldToNewIdMap.has(cellId)) {
|
||||||
|
newCells[oldToNewIdMap.get(cellId)!] = originalCells[cellId];
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
payload.model.props.cells$.value = newCells;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
slots.afterImport.subscribe(payload => {
|
return () => {
|
||||||
if (
|
beforeImportSliceSubscription.unsubscribe();
|
||||||
!samePage &&
|
afterImportBlockSubscription.unsubscribe();
|
||||||
payload.type === 'block' &&
|
};
|
||||||
matchModels(payload.model, [DatabaseBlockModel])
|
|
||||||
) {
|
|
||||||
const originalCells = payload.model.props.cells;
|
|
||||||
const newCells = {
|
|
||||||
...originalCells,
|
|
||||||
};
|
|
||||||
|
|
||||||
Object.keys(originalCells).forEach(cellId => {
|
|
||||||
if (oldToNewIdMap.has(cellId)) {
|
|
||||||
newCells[oldToNewIdMap.get(cellId)!] = originalCells[cellId];
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
payload.model.props.cells$.value = newCells;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -7,19 +7,25 @@ import type { TransformerMiddleware } from '@blocksuite/store';
|
|||||||
export const reorderList =
|
export const reorderList =
|
||||||
(std: BlockStdScope): TransformerMiddleware =>
|
(std: BlockStdScope): TransformerMiddleware =>
|
||||||
({ slots }) => {
|
({ slots }) => {
|
||||||
slots.afterImport.subscribe(payload => {
|
const afterImportBlockSubscription = slots.afterImport.subscribe(
|
||||||
if (payload.type === 'block') {
|
payload => {
|
||||||
const model = payload.model;
|
if (payload.type === 'block') {
|
||||||
if (
|
const model = payload.model;
|
||||||
matchModels(model, [ListBlockModel]) &&
|
if (
|
||||||
model.props.type === 'numbered'
|
matchModels(model, [ListBlockModel]) &&
|
||||||
) {
|
model.props.type === 'numbered'
|
||||||
const next = std.store.getNext(model);
|
) {
|
||||||
correctNumberedListsOrderToPrev(std.store, model);
|
const next = std.store.getNext(model);
|
||||||
if (next) {
|
correctNumberedListsOrderToPrev(std.store, model);
|
||||||
correctNumberedListsOrderToPrev(std.store, next);
|
if (next) {
|
||||||
|
correctNumberedListsOrderToPrev(std.store, next);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
afterImportBlockSubscription.unsubscribe();
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -113,6 +113,8 @@ type TransformerMiddlewareOptions = {
|
|||||||
transformerConfigs: Map<string, unknown>;
|
transformerConfigs: Map<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type TransformerMiddlewareCleanup = () => void;
|
||||||
|
|
||||||
export type TransformerMiddleware = (
|
export type TransformerMiddleware = (
|
||||||
options: TransformerMiddlewareOptions
|
options: TransformerMiddlewareOptions
|
||||||
) => void;
|
) => void | TransformerMiddlewareCleanup;
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
import { DisposableGroup } from '@blocksuite/global/disposable';
|
||||||
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
import { BlockSuiteError, ErrorCode } from '@blocksuite/global/exceptions';
|
||||||
import { nextTick } from '@blocksuite/global/utils';
|
import { nextTick } from '@blocksuite/global/utils';
|
||||||
import { Subject } from 'rxjs';
|
import { Subject } from 'rxjs';
|
||||||
@@ -67,6 +68,8 @@ export class Transformer {
|
|||||||
|
|
||||||
private readonly _docCRUD: DocCRUD;
|
private readonly _docCRUD: DocCRUD;
|
||||||
|
|
||||||
|
private readonly _disposables: DisposableGroup = new DisposableGroup();
|
||||||
|
|
||||||
private readonly _slots: TransformerSlots = {
|
private readonly _slots: TransformerSlots = {
|
||||||
beforeImport: new Subject<BeforeImportPayload>(),
|
beforeImport: new Subject<BeforeImportPayload>(),
|
||||||
afterImport: new Subject<AfterImportPayload>(),
|
afterImport: new Subject<AfterImportPayload>(),
|
||||||
@@ -366,13 +369,16 @@ export class Transformer {
|
|||||||
this._docCRUD = docCRUD;
|
this._docCRUD = docCRUD;
|
||||||
|
|
||||||
middlewares.forEach(middleware => {
|
middlewares.forEach(middleware => {
|
||||||
middleware({
|
const cleanup = middleware({
|
||||||
slots: this._slots,
|
slots: this._slots,
|
||||||
docCRUD: this._docCRUD,
|
docCRUD: this._docCRUD,
|
||||||
assetsManager: this._assetsManager,
|
assetsManager: this._assetsManager,
|
||||||
adapterConfigs: this._adapterConfigs,
|
adapterConfigs: this._adapterConfigs,
|
||||||
transformerConfigs: this._transformerConfigs,
|
transformerConfigs: this._transformerConfigs,
|
||||||
});
|
});
|
||||||
|
if (cleanup) {
|
||||||
|
this._disposables.add(cleanup);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -646,4 +652,9 @@ export class Transformer {
|
|||||||
reset() {
|
reset() {
|
||||||
this._assetsManager.cleanup();
|
this._assetsManager.cleanup();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Symbol.dispose]() {
|
||||||
|
this._disposables.dispose();
|
||||||
|
this._assetsManager.cleanup();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user