refactor(core): move block collection to affine and implement as doc (#9514)

This commit is contained in:
Saul-Mirone
2025-01-04 06:28:54 +00:00
parent 4cb186def2
commit dcf4993265
39 changed files with 595 additions and 192 deletions
@@ -167,7 +167,7 @@ export class BlockQueryDataSource extends DataSourceBase {
type ?? propertyPresets.multiSelectPropertyConfig.type
].create(this.newColumnName());
const id = doc.generateBlockId();
const id = doc.collection.idGenerator();
if (this.block.columns.some(v => v.id === id)) {
return id;
}
@@ -33,7 +33,7 @@ export class DataViewBlockModel extends BlockModel<Props> {
}
duplicateView(id: string): string {
const newId = this.doc.generateBlockId();
const newId = this.doc.collection.idGenerator();
this.doc.transact(() => {
const index = this.views.findIndex(v => v.id === id);
const view = this.views[index];
@@ -448,7 +448,7 @@ export const databaseViewInitTemplate = (
const rowId = model.doc.addBlock(
'affine:paragraph',
{
text: new model.doc.Text(`Task ${i + 1}`),
text: new Text(`Task ${i + 1}`),
},
model.id
);
@@ -19,7 +19,7 @@ export function addProperty(
id?: string;
}
): string {
const id = column.id ?? model.doc.generateBlockId();
const id = column.id ?? model.doc.collection.idGenerator();
if (model.columns.some(v => v.id === id)) {
return id;
}
@@ -101,7 +101,7 @@ export function deleteView(model: DatabaseBlockModel, id: string) {
}
export function duplicateView(model: DatabaseBlockModel, id: string): string {
const newId = model.doc.generateBlockId();
const newId = model.doc.collection.idGenerator();
model.doc.transact(() => {
const index = model.views.findIndex(v => v.id === id);
const view = model.views[index];
@@ -17,6 +17,7 @@ import {
type DraftModel,
type Query,
Slice,
Text,
} from '@blocksuite/store';
import { render, type TemplateResult } from 'lit';
@@ -407,7 +408,7 @@ export function createLinkedDocFromSlice(
const linkedDoc = doc.collection.createDoc({});
linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', {
title: new doc.Text(docTitle),
title: new Text(docTitle),
});
linkedDoc.addBlock('affine:surface', {}, rootId);
const noteId = linkedDoc.addBlock('affine:note', {}, rootId);
@@ -22,6 +22,7 @@ import {
referenceToNode,
} from '@blocksuite/affine-shared/utils';
import { Bound, throttle } from '@blocksuite/global/utils';
import { Text } from '@blocksuite/store';
import { computed } from '@preact/signals-core';
import { html, nothing } from 'lit';
import { property, queryAsync, state } from 'lit/decorators.js';
@@ -171,7 +172,7 @@ export class EmbedLinkedDocBlockComponent extends EmbedBlockComponent<EmbedLinke
...this.referenceInfo$.peek(),
},
});
const text = new doc.Text(yText);
const text = new Text(yText);
doc.addBlock(
'affine:paragraph',
@@ -29,6 +29,7 @@ import {
BlockViewType,
type GetDocOptions,
type Query,
Text,
} from '@blocksuite/store';
import { computed } from '@preact/signals-core';
import { html, type PropertyValues } from 'lit';
@@ -280,7 +281,7 @@ export class EmbedSyncedDocBlockComponent extends EmbedBlockComponent<EmbedSynce
...this.referenceInfo,
},
});
const text = new doc.Text(yText);
const text = new Text(yText);
doc.addBlock(
'affine:paragraph',
@@ -1,6 +1,7 @@
import { focusTextModel } from '@blocksuite/affine-components/rich-text';
import { getLastNoteBlock } from '@blocksuite/affine-shared/utils';
import type { Command } from '@blocksuite/block-std';
import { Text } from '@blocksuite/store';
/**
* Append a paragraph block at the end of the whole page.
@@ -19,11 +20,7 @@ export const appendParagraphCommand: Command<
if (!noteId) {
noteId = doc.addBlock('affine:note', {}, doc.root.id);
}
const id = doc.addBlock(
'affine:paragraph',
{ text: new doc.Text(text) },
noteId
);
const id = doc.addBlock('affine:paragraph', { text: new Text(text) }, noteId);
focusTextModel(std, id, text.length);
next();
@@ -1,4 +1,5 @@
import type { Workspace } from '@blocksuite/store';
import { Text } from '@blocksuite/store';
export function createDefaultDoc(
collection: Workspace,
@@ -9,7 +10,7 @@ export function createDefaultDoc(
doc.load();
const title = options.title ?? '';
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(title),
title: new Text(title),
});
collection.meta.setDocMeta(doc.id, {
title,
@@ -18,7 +18,7 @@ import {
} from '@blocksuite/affine-model';
import { propertyModelPresets } from '@blocksuite/data-view/property-pure-presets';
import type { BlockModel, Blocks } from '@blocksuite/store';
import { Schema } from '@blocksuite/store';
import { Schema, Text } from '@blocksuite/store';
import {
createAutoIncrementIdGenerator,
TestWorkspace,
@@ -71,7 +71,7 @@ describe('DatabaseManager', () => {
doc = createTestDoc();
rootId = doc.addBlock('affine:page', {
title: new doc.Text('database test'),
title: new Text('database test'),
});
noteBlockId = doc.addBlock('affine:note', {}, rootId);
@@ -114,14 +114,14 @@ describe('DatabaseManager', () => {
p1 = doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('text1'),
text: new Text('text1'),
},
databaseBlockId
);
p2 = doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('text2'),
text: new Text('text2'),
},
databaseBlockId
);
@@ -173,7 +173,7 @@ describe('DatabaseManager', () => {
const modelId = doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('paragraph'),
text: new Text('paragraph'),
},
noteBlockId
);
@@ -201,7 +201,7 @@ describe('DatabaseManager', () => {
const newRowId = doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('text3'),
text: new Text('text3'),
},
databaseBlockId
);
@@ -5,7 +5,7 @@ import { DocModeProvider } from '@blocksuite/affine-shared/services';
import { getBlockProps } from '@blocksuite/affine-shared/utils';
import type { EditorHost } from '@blocksuite/block-std';
import { GfxBlockElementModel } from '@blocksuite/block-std/gfx';
import { type BlockModel, type Blocks } from '@blocksuite/store';
import { type BlockModel, type Blocks, Text } from '@blocksuite/store';
import {
getElementProps,
@@ -43,7 +43,7 @@ export function createLinkedDocFromNote(
const linkedDoc = doc.collection.createDoc({});
linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', {
title: new doc.Text(docTitle),
title: new Text(docTitle),
});
linkedDoc.addBlock('affine:surface', {}, rootId);
const blockProps = getBlockProps(note);
@@ -74,7 +74,7 @@ export function createLinkedDocFromEdgelessElements(
const linkedDoc = host.doc.collection.createDoc({});
linkedDoc.load(() => {
const rootId = linkedDoc.addBlock('affine:page', {
title: new host.doc.Text(docTitle),
title: new Text(docTitle),
});
const surfaceId = linkedDoc.addBlock('affine:surface', {}, rootId);
const surface = getSurfaceBlock(linkedDoc);
@@ -53,7 +53,7 @@ import {
} from '@blocksuite/affine-shared/services';
import { getHostName, referenceToNode } from '@blocksuite/affine-shared/utils';
import { type BlockStdScope, WidgetComponent } from '@blocksuite/block-std';
import { type BlockModel } from '@blocksuite/store';
import { type BlockModel, Text } from '@blocksuite/store';
import { autoUpdate, computePosition, flip, offset } from '@floating-ui/dom';
import { html, nothing, type TemplateResult } from 'lit';
import { query, state } from 'lit/decorators.js';
@@ -619,7 +619,7 @@ export class EmbedCardToolbar extends WidgetComponent<
const insert = title || caption || url;
yText.insert(0, insert);
yText.format(0, insert.length, { link: url });
const text = new doc.Text(yText);
const text = new Text(yText);
doc.addBlock(
'affine:paragraph',
{
@@ -709,9 +709,7 @@ export const defaultSlashMenuConfig: SlashMenuConfig = {
model.flavour as never,
{
type: (model as ParagraphBlockModel).type,
text: new rootComponent.doc.Text(
model.text.toDelta() as DeltaInsert[]
),
text: new Text(model.text.toDelta() as DeltaInsert[]),
// @ts-expect-error FIXME: ts error
checked: model.checked,
},
@@ -7,6 +7,7 @@ import { applyUpdate, encodeStateAsUpdate } from 'yjs';
import { COLLECTION_VERSION, PAGE_VERSION } from '../consts.js';
import type { BlockModel, Blocks, BlockSchemaType } from '../index.js';
import { Schema } from '../index.js';
import { Text } from '../reactive/text.js';
import type { DocMeta } from '../store/workspace.js';
import { TestWorkspace } from '../test/test-workspace.js';
import { createAutoIncrementIdGenerator } from '../utils/id-generator.js';
@@ -166,7 +167,7 @@ describe('basic', () => {
doc.load(() => {
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new Text(),
});
expect(rootAddedCallback).toBeCalledTimes(1);
@@ -186,7 +187,7 @@ describe('basic', () => {
});
doc.load(() => {
doc.addBlock('affine:page', {
title: new doc.Text(),
title: new Text(),
});
});
{
@@ -245,7 +246,7 @@ describe('addBlock', () => {
it('can add single model', () => {
const doc = createTestDoc();
doc.addBlock('affine:page', {
title: new doc.Text(),
title: new Text(),
});
assert.deepEqual(serializCollection(doc.rootDoc).spaces[spaceId].blocks, {
@@ -264,7 +265,7 @@ describe('addBlock', () => {
it('can add model with props', () => {
const doc = createTestDoc();
doc.addBlock('affine:page', { title: new doc.Text('hello') });
doc.addBlock('affine:page', { title: new Text('hello') });
assert.deepEqual(serializCollection(doc.rootDoc).spaces[spaceId].blocks, {
'0': {
@@ -283,7 +284,7 @@ describe('addBlock', () => {
it('can add multi models', () => {
const doc = createTestDoc();
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new Text(),
});
const noteId = doc.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:paragraph', {}, noteId);
@@ -344,7 +345,7 @@ describe('addBlock', () => {
queueMicrotask(() =>
doc.addBlock('affine:page', {
title: new doc.Text(),
title: new Text(),
})
);
const blockId = await waitOnce(doc.slots.rootAdded);
@@ -389,7 +390,7 @@ describe('addBlock', () => {
assert.equal(collection.docs.size, 2);
doc0.addBlock('affine:page', {
title: new doc0.Text(),
title: new Text(),
});
collection.removeDoc(doc0.id);
@@ -5,15 +5,15 @@ import { signal } from '@preact/signals-core';
import type { BlockModel, Schema } from '../../schema/index.js';
import type { DraftModel } from '../../transformer/index.js';
import { syncBlockProps } from '../../utils/utils.js';
import type { BlockProps, Doc } from '../workspace.js';
import type { BlockOptions } from './block/index.js';
import { Block } from './block/index.js';
import type { BlockCollection, BlockProps } from './block-collection.js';
import { DocCRUD } from './crud.js';
import { type Query, runQuery } from './query.js';
type DocOptions = {
schema: Schema;
blockCollection: BlockCollection;
blockCollection: Doc;
readonly?: boolean;
query?: Query;
};
@@ -23,7 +23,7 @@ export class Blocks {
runQuery(this._query, block);
};
protected readonly _blockCollection: BlockCollection;
protected readonly _blockCollection: Doc;
protected readonly _blocks = signal<Record<string, Block>>({});
@@ -40,7 +40,7 @@ export class Blocks {
protected readonly _schema: Schema;
readonly slots: BlockCollection['slots'] & {
readonly slots: Doc['slots'] & {
/** This is always triggered after `doc.load` is called. */
ready: Slot;
/**
@@ -179,10 +179,6 @@ export class Blocks {
return this._blockCollection.collection;
}
get generateBlockId() {
return this._blockCollection.generateBlockId.bind(this._blockCollection);
}
get history() {
return this._blockCollection.history;
}
@@ -250,10 +246,6 @@ export class Blocks {
return this._blockCollection.spaceDoc;
}
get Text() {
return this._blockCollection.Text;
}
get transact() {
return this._blockCollection.transact.bind(this._blockCollection);
}
@@ -432,7 +424,7 @@ export class Blocks {
);
}
const id = blockProps.id ?? this._blockCollection.generateBlockId();
const id = blockProps.id ?? this._blockCollection.collection.idGenerator();
this.transact(() => {
this._crud.addBlock(
@@ -1,5 +1,4 @@
export * from './block/index.js';
export * from './block-collection.js';
export * from './consts.js';
export * from './doc.js';
export * from './query.js';
@@ -1,4 +1,3 @@
export type * from './doc/block-collection.js';
export * from './doc/index.js';
export * from './meta.js';
export * from './workspace.js';
@@ -1,12 +1,14 @@
import type { Slot } from '@blocksuite/global/utils';
import type { BlobEngine, DocEngine } from '@blocksuite/sync';
import type * as Y from 'yjs';
import type { BlockModel } from '../schema/base.js';
import type { Schema } from '../schema/schema.js';
import type { IdGenerator } from '../utils/id-generator.js';
import type { AwarenessStore } from '../yjs/awareness.js';
import type { BlockSuiteDoc } from '../yjs/doc.js';
import type { YBlock } from './doc/block/types.js';
import type { Blocks } from './doc/doc.js';
import type { BlockCollection } from './doc/index.js';
import type { Query } from './doc/query.js';
export type Tag = {
@@ -70,7 +72,7 @@ export interface Workspace {
get schema(): Schema;
get doc(): BlockSuiteDoc;
get docs(): Map<string, BlockCollection>;
get docs(): Map<string, Doc>;
slots: {
docListUpdated: Slot;
@@ -85,6 +87,77 @@ export interface Workspace {
dispose(): void;
}
export interface Doc {
readonly id: string;
get meta(): DocMeta | undefined;
get schema(): Schema;
remove(): void;
load(initFn?: () => void): void;
get ready(): boolean;
dispose(): void;
slots: {
historyUpdated: Slot;
yBlockUpdated: Slot<
| {
type: 'add';
id: string;
}
| {
type: 'delete';
id: string;
}
>;
};
get canRedo(): boolean;
get canUndo(): boolean;
undo(): void;
redo(): void;
resetHistory(): void;
transact(fn: () => void, shouldTransact?: boolean): void;
withoutTransact(fn: () => void): void;
captureSync(): void;
clear(): void;
getDoc(options?: GetDocOptions): Blocks;
clearQuery(query: Query, readonly?: boolean): void;
get history(): Y.UndoManager;
get loaded(): boolean;
get readonly(): boolean;
get awarenessStore(): AwarenessStore;
get collection(): Workspace;
get rootDoc(): BlockSuiteDoc;
get spaceDoc(): Y.Doc;
get yBlocks(): Y.Map<YBlock>;
}
export interface StackItem {
meta: Map<'selection-state', unknown>;
}
export type YBlocks = Y.Map<YBlock>;
/** JSON-serializable properties of a block */
export type BlockSysProps = {
id: string;
flavour: string;
children?: BlockModel[];
};
export type BlockProps = BlockSysProps & Record<string, unknown>;
declare global {
namespace BlockSuite {
interface BlockModels {}
type Flavour = string & keyof BlockModels;
type ModelProps<Model> = Partial<
Model extends BlockModel<infer U> ? U : never
>;
}
}
@@ -1,2 +1,3 @@
export { createAutoIncrementIdGenerator } from '../utils/id-generator.js';
export * from './test-doc.js';
export * from './test-workspace.js';
@@ -1,36 +1,21 @@
import { type Disposable, Slot } from '@blocksuite/global/utils';
import { signal } from '@preact/signals-core';
import { uuidv4 } from 'lib0/random.js';
import * as Y from 'yjs';
import { Text } from '../../reactive/text.js';
import type { BlockModel } from '../../schema/base.js';
import type { IdGenerator } from '../../utils/id-generator.js';
import type { AwarenessStore, BlockSuiteDoc } from '../../yjs/index.js';
import type { GetDocOptions, Workspace } from '../workspace.js';
import { Blocks } from './doc.js';
import type { YBlock } from './index.js';
import type { Query } from './query.js';
export type YBlocks = Y.Map<YBlock>;
/** JSON-serializable properties of a block */
export type BlockSysProps = {
id: string;
flavour: string;
children?: BlockModel[];
};
export type BlockProps = BlockSysProps & Record<string, unknown>;
import { Blocks } from '../store/doc/doc.js';
import type { YBlock } from '../store/doc/index.js';
import type { Query } from '../store/doc/query.js';
import type { Doc, GetDocOptions, Workspace } from '../store/workspace.js';
import type { AwarenessStore, BlockSuiteDoc } from '../yjs/index.js';
type DocOptions = {
id: string;
collection: Workspace;
doc: BlockSuiteDoc;
awarenessStore: AwarenessStore;
idGenerator?: IdGenerator;
};
export class BlockCollection {
export class TestDoc implements Doc {
private _awarenessUpdateDisposable: Disposable | null = null;
private readonly _canRedo$ = signal(false);
@@ -57,8 +42,6 @@ export class BlockCollection {
this.slots.historyUpdated.emit();
};
private readonly _idGenerator: IdGenerator;
private readonly _initSubDoc = () => {
let subDoc = this.rootDoc.spaces.get(this.id);
if (!subDoc) {
@@ -184,7 +167,7 @@ export class BlockCollection {
return this.collection.meta.getDocMeta(this.id);
}
get readonly() {
get readonly(): boolean {
return this.awarenessStore.isReadonly(this);
}
@@ -200,21 +183,11 @@ export class BlockCollection {
return this._ySpaceDoc;
}
get Text() {
return Text;
}
get yBlocks() {
return this._yBlocks;
}
constructor({
id,
collection,
doc,
awarenessStore,
idGenerator = uuidv4,
}: DocOptions) {
constructor({ id, collection, doc, awarenessStore }: DocOptions) {
this.id = id;
this.rootDoc = doc;
this.awarenessStore = awarenessStore;
@@ -223,7 +196,6 @@ export class BlockCollection {
this._yBlocks = this._ySpaceDoc.getMap('blocks');
this._collection = collection;
this._idGenerator = idGenerator;
}
private _getReadonlyKey(readonly?: boolean): 'true' | 'false' | 'undefined' {
@@ -295,7 +267,7 @@ export class BlockCollection {
this._docMap[readonlyKey].delete(JSON.stringify(query));
}
destroy() {
private _destroy() {
this._ySpaceDoc.destroy();
this._onLoadSlot.dispose();
this._loaded = false;
@@ -311,10 +283,6 @@ export class BlockCollection {
}
}
generateBlockId() {
return this._idGenerator();
}
getDoc({ readonly, query }: GetDocOptions = {}) {
const readonlyKey = this._getReadonlyKey(readonly);
@@ -375,8 +343,16 @@ export class BlockCollection {
this._history.redo();
}
undo() {
if (this.readonly) {
console.error('cannot modify data in readonly mode');
return;
}
this._history.undo();
}
remove() {
this.destroy();
this._destroy();
this.rootDoc.spaces.delete(this.id);
}
@@ -403,30 +379,9 @@ export class BlockCollection {
);
}
// Handle all the events that happen at _any_ level (potentially deep inside the structure).
undo() {
if (this.readonly) {
console.error('cannot modify data in readonly mode');
return;
}
this._history.undo();
}
withoutTransact(callback: () => void) {
this._shouldTransact = false;
callback();
this._shouldTransact = true;
}
}
declare global {
namespace BlockSuite {
interface BlockModels {}
type Flavour = string & keyof BlockModels;
type ModelProps<Model> = Partial<
Model extends BlockModel<infer U> ? U : never
>;
}
}
@@ -17,7 +17,6 @@ import { Awareness } from 'y-protocols/awareness.js';
import type { Schema } from '../schema/index.js';
import {
BlockCollection,
type Blocks,
type CreateDocOptions,
DocCollectionMeta,
@@ -30,6 +29,7 @@ import {
BlockSuiteDoc,
type RawAwarenessState,
} from '../yjs/index.js';
import { TestDoc } from './test-doc.js';
export type DocCollectionOptions = {
schema: Schema;
@@ -80,7 +80,7 @@ export class TestWorkspace implements Workspace {
readonly blobSync: BlobEngine;
readonly blockCollections = new Map<string, BlockCollection>();
readonly blockCollections = new Map<string, TestDoc>();
readonly doc: BlockSuiteDoc;
@@ -154,12 +154,11 @@ export class TestWorkspace implements Workspace {
private _bindDocMetaEvents() {
this.meta.docMetaAdded.on(docId => {
const doc = new BlockCollection({
const doc = new TestDoc({
id: docId,
collection: this,
doc: this.doc,
awarenessStore: this.awarenessStore,
idGenerator: this.idGenerator,
});
this.blockCollections.set(doc.id, doc);
});
@@ -225,8 +224,8 @@ export class TestWorkspace implements Workspace {
this.awarenessSync.disconnect();
}
getBlockCollection(docId: string): BlockCollection | null {
const space = this.docs.get(docId) as BlockCollection | undefined;
getBlockCollection(docId: string): TestDoc | null {
const space = this.docs.get(docId) as TestDoc | undefined;
return space ?? null;
}
@@ -5,7 +5,7 @@ import { native2Y } from '../reactive/index.js';
import type { BlockModel, BlockSchema } from '../schema/base.js';
import { internalPrimitives } from '../schema/base.js';
import type { YBlock } from '../store/doc/block/index.js';
import type { BlockProps } from '../store/doc/block-collection.js';
import type { BlockProps } from '../store/workspace.js';
export function syncBlockProps(
schema: z.infer<typeof BlockSchema>,
@@ -5,7 +5,7 @@ import clonedeep from 'lodash.clonedeep';
import merge from 'lodash.merge';
import type { Awareness as YAwareness } from 'y-protocols/awareness.js';
import type { BlockCollection } from '../store/index.js';
import type { Doc } from '../store/index.js';
export interface UserInfo {
name: string;
@@ -115,7 +115,7 @@ export class AwarenessStore<Flags extends BlockSuiteFlags = BlockSuiteFlags> {
return this.awareness.getStates();
}
isReadonly(blockCollection: BlockCollection): boolean {
isReadonly(blockCollection: Doc): boolean {
const rd = this.getFlag('readonly');
if (rd && typeof rd === 'object') {
return Boolean((rd as Record<string, boolean>)[blockCollection.id]);
@@ -137,7 +137,7 @@ export class AwarenessStore<Flags extends BlockSuiteFlags = BlockSuiteFlags> {
});
}
setReadonly(blockCollection: BlockCollection, value: boolean): void {
setReadonly(blockCollection: Doc, value: boolean): void {
const flags = this.getFlag('readonly') ?? {};
this.setFlag('readonly', {
...flags,
@@ -1,5 +1,5 @@
import { effects as blocksEffects } from '@blocksuite/blocks/effects';
import type { BlockCollection, Blocks, Job } from '@blocksuite/store';
import type { Blocks, Job } from '@blocksuite/store';
import { effects } from '../../effects.js';
@@ -57,9 +57,7 @@ function initCollection(collection: TestWorkspace) {
async function createEditor(collection: TestWorkspace, mode: DocMode = 'page') {
const app = document.createElement('div');
const blockCollection = collection.docs.values().next().value as
| BlockCollection
| undefined;
const blockCollection = collection.docs.values().next().value;
assertExists(blockCollection, 'Need to create a doc first');
const doc = blockCollection.getDoc();
const editor = new AffineEditorContainer();
+3 -3
View File
@@ -64,11 +64,11 @@ test(scoped`basic init with external text`, async ({ page }) => {
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text('hello'),
title: new window.$blocksuite.store.Text('hello'),
});
const note = doc.addBlock('affine:note', {}, rootId);
const text = new doc.Text('world');
const text = new window.$blocksuite.store.Text('world');
doc.addBlock('affine:paragraph', { text }, note);
const delta = [
@@ -78,7 +78,7 @@ test(scoped`basic init with external text`, async ({ page }) => {
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text(delta as DeltaInsert[]),
text: new window.$blocksuite.store.Text(delta as DeltaInsert[]),
},
note
);
+2 -2
View File
@@ -628,7 +628,7 @@ test.fixme(
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
doc.addBlock('affine:surface', {}, rootId);
@@ -637,7 +637,7 @@ test.fixme(
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text(text),
text: new window.$blocksuite.store.Text(text),
},
noteId
);
@@ -554,7 +554,7 @@ test('press backspace at the start of first line when edgeless text exist', asyn
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
doc.addBlock('affine:surface', {}, rootId);
doc.addBlock('affine:note', {}, rootId);
@@ -140,7 +140,7 @@ test.describe('Embed synced doc', () => {
await page.evaluate(() => {
const { doc, collection } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const noteId = doc.addBlock('affine:note', {}, rootId);
@@ -149,14 +149,14 @@ test.describe('Embed synced doc', () => {
const doc2 = collection.createDoc({ id: 'doc2' });
doc2.load();
const rootId2 = doc2.addBlock('affine:page', {
title: new doc.Text('Doc 2'),
title: new window.$blocksuite.store.Text('Doc 2'),
});
const noteId2 = doc2.addBlock('affine:note', {}, rootId2);
doc2.addBlock(
'affine:paragraph',
{
text: new doc.Text('Hello from Doc 2'),
text: new window.$blocksuite.store.Text('Hello from Doc 2'),
},
noteId2
);
@@ -164,14 +164,14 @@ test.describe('Embed synced doc', () => {
const doc3 = collection.createDoc({ id: 'doc3' });
doc3.load();
const rootId3 = doc3.addBlock('affine:page', {
title: new doc.Text('Doc 3'),
title: new window.$blocksuite.store.Text('Doc 3'),
});
const noteId3 = doc3.addBlock('affine:note', {}, rootId3);
doc3.addBlock(
'affine:paragraph',
{
text: new doc.Text('Hello from Doc 3'),
text: new window.$blocksuite.store.Text('Hello from Doc 3'),
},
noteId3
);
@@ -232,7 +232,7 @@ test.describe('Embed synced doc', () => {
const databaseId = doc2.addBlock(
'affine:database',
{
title: new doc2.Text('Database 1'),
title: new window.$blocksuite.store.Text('Database 1'),
},
noteId
);
+6 -6
View File
@@ -453,10 +453,10 @@ test('should format quick bar position correct at the start of second line', asy
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
const text = new doc.Text('a'.repeat(100));
const text = new window.$blocksuite.store.Text('a'.repeat(100));
const paragraphId = doc.addBlock('affine:paragraph', { text }, note);
return paragraphId;
});
@@ -715,7 +715,7 @@ test('should format bar style active correctly', async ({ page }) => {
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
const delta = [
@@ -723,7 +723,7 @@ test('should format bar style active correctly', async ({ page }) => {
{ insert: '2', attributes: { bold: true, underline: true } },
{ insert: '3', attributes: { bold: true, code: true } },
];
const text = new doc.Text(delta as DeltaInsert[]);
const text = new window.$blocksuite.store.Text(delta as DeltaInsert[]);
doc.addBlock('affine:paragraph', { text }, note);
});
@@ -869,7 +869,7 @@ test('should update the format quick bar state when there is a change in keyboar
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
const delta = [
@@ -877,7 +877,7 @@ test('should update the format quick bar state when there is a change in keyboar
{ insert: '2', attributes: { bold: true } },
{ insert: '3', attributes: { bold: false } },
];
const text = new doc.Text(delta as DeltaInsert[]);
const text = new window.$blocksuite.store.Text(delta as DeltaInsert[]);
doc.addBlock('affine:paragraph', { text }, note);
});
await focusTitle(page);
+2 -2
View File
@@ -121,11 +121,11 @@ async function createLinkBlock(page: Page, str: string, link: string) {
([str, link]) => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text('title'),
title: new window.$blocksuite.store.Text('title'),
});
const noteId = doc.addBlock('affine:note', {}, rootId);
const text = new doc.Text([
const text = new window.$blocksuite.store.Text([
{ insert: 'Hello' },
{ insert: str, attributes: { link } },
]);
+14 -12
View File
@@ -825,20 +825,20 @@ test('press arrow down should move caret to the start of line', async ({
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('0'.repeat(100)),
text: new window.$blocksuite.store.Text('0'.repeat(100)),
},
note
);
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('1'),
text: new window.$blocksuite.store.Text('1'),
},
note
);
@@ -860,7 +860,7 @@ test('press arrow up in the second line should move caret to the first line', as
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
const delta = Array.from({ length: 150 }, (_, i) => {
@@ -868,7 +868,7 @@ test('press arrow up in the second line should move caret to the first line', as
? { insert: 'i', attributes: { italic: true } }
: { insert: 'b', attributes: { bold: true } };
}) as DeltaInsert[];
const text = new doc.Text(delta);
const text = new window.$blocksuite.store.Text(delta);
doc.addBlock('affine:paragraph', { text }, note);
doc.addBlock('affine:paragraph', {}, note);
});
@@ -912,7 +912,7 @@ test('press arrow down in indent line should not move caret to the start of line
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
const p1 = doc.addBlock('affine:paragraph', {}, note);
@@ -921,7 +921,7 @@ test('press arrow down in indent line should not move caret to the start of line
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('0'),
text: new window.$blocksuite.store.Text('0'),
},
note
);
@@ -1003,20 +1003,22 @@ test.describe('press ArrowDown when cursor is at the last line of a block', () =
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('This is the 2nd last block.'),
text: new window.$blocksuite.store.Text(
'This is the 2nd last block.'
),
},
note
);
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text('This is the last block.'),
text: new window.$blocksuite.store.Text('This is the last block.'),
},
note
);
@@ -1172,7 +1174,7 @@ test('delete at the start of paragraph (multiple notes)', async ({ page }) => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
doc.addBlock('affine:surface', {}, rootId);
@@ -1181,7 +1183,7 @@ test('delete at the start of paragraph (multiple notes)', async ({ page }) => {
doc.addBlock(
'affine:paragraph',
{
text: new doc.Text(text),
text: new window.$blocksuite.store.Text(text),
},
noteId
);
+14 -14
View File
@@ -413,7 +413,7 @@ export async function enterPlaygroundWithList(
({ contents, type }: { contents: string[]; type: ListType }) => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const noteId = doc.addBlock('affine:note', {}, rootId);
// eslint-disable-next-line @typescript-eslint/prefer-for-of
@@ -421,7 +421,7 @@ export async function enterPlaygroundWithList(
doc.addBlock(
'affine:list',
contents.length > 0
? { text: new doc.Text(contents[i]), type }
? { text: new window.$blocksuite.store.Text(contents[i]), type }
: { type },
noteId
);
@@ -439,7 +439,7 @@ export async function initEmptyParagraphState(page: Page, rootId?: string) {
doc.captureSync();
if (!rootId) {
rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
}
@@ -464,7 +464,7 @@ export async function initMultipleNoteWithParagraphState(
doc.captureSync();
if (!rootId) {
rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
}
@@ -490,7 +490,7 @@ export async function initEmptyEdgelessState(page: Page) {
const ids = await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
doc.addBlock('affine:surface', {}, rootId);
const noteId = doc.addBlock('affine:note', {}, rootId);
@@ -509,14 +509,14 @@ export async function initEmptyDatabaseState(page: Page, rootId?: string) {
doc.captureSync();
if (!rootId) {
rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
}
const noteId = doc.addBlock('affine:note', {}, rootId);
const databaseId = doc.addBlock(
'affine:database',
{
title: new doc.Text('Database 1'),
title: new window.$blocksuite.store.Text('Database 1'),
},
noteId
);
@@ -553,14 +553,14 @@ export async function initKanbanViewState(
doc.captureSync();
if (!rootId) {
rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
}
const noteId = doc.addBlock('affine:note', {}, rootId);
const databaseId = doc.addBlock(
'affine:database',
{
title: new doc.Text('Database 1'),
title: new window.$blocksuite.store.Text('Database 1'),
},
noteId
);
@@ -572,7 +572,7 @@ export async function initKanbanViewState(
const rowIds = config.rows.map(rowText => {
const rowId = doc.addBlock(
'affine:paragraph',
{ type: 'text', text: new doc.Text(rowText) },
{ type: 'text', text: new window.$blocksuite.store.Text(rowText) },
databaseId
);
return rowId;
@@ -590,7 +590,7 @@ export async function initKanbanViewState(
columnId,
value:
column.type === 'rich-text'
? new doc.Text(value as string)
? new window.$blocksuite.store.Text(value as string)
: value,
});
}
@@ -619,14 +619,14 @@ export async function initEmptyDatabaseWithParagraphState(
doc.captureSync();
if (!rootId) {
rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
}
const noteId = doc.addBlock('affine:note', {}, rootId);
const databaseId = doc.addBlock(
'affine:database',
{
title: new doc.Text('Database 1'),
title: new window.$blocksuite.store.Text('Database 1'),
},
noteId
);
@@ -1326,7 +1326,7 @@ export async function initImageState(page: Page, prependParagraph = false) {
await page.evaluate(async prepend => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const noteId = doc.addBlock('affine:note', {}, rootId);
@@ -6,7 +6,7 @@ import type {
} from '@blocksuite/block-std';
import type { AffineEditorContainer } from '@blocksuite/presets';
import type { StarterDebugMenu } from '@playground/apps/_common/components/starter-debug-menu.js';
import type { BlockModel, Doc, Job, Workspace } from '@store/index.js';
import type { BlockModel, Blocks, Job, Workspace } from '@store/index.js';
declare global {
interface Window {
@@ -38,7 +38,7 @@ declare global {
};
collection: Workspace;
blockSchema: Record<string, typeof BlockModel>;
doc: Doc;
doc: Blocks;
debugMenu: StarterDebugMenu;
editor: AffineEditorContainer;
host: EditorHost;
+2 -2
View File
@@ -44,7 +44,7 @@ test(
await page.evaluate(() => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:code', {}, note);
@@ -71,7 +71,7 @@ test(
async ({ bookMarkUrl, embedUrl }) => {
const { doc } = window;
const rootId = doc.addBlock('affine:page', {
title: new doc.Text(),
title: new window.$blocksuite.store.Text(),
});
const note = doc.addBlock('affine:note', {}, rootId);
doc.addBlock('affine:code', {}, note);
@@ -1,5 +1,5 @@
import type { DeltaInsert } from '@blocksuite/affine/inline';
import type { Workspace } from '@blocksuite/affine/store';
import { Text, type Workspace } from '@blocksuite/affine/store';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import { useCallback } from 'react';
@@ -10,7 +10,7 @@ export function useReferenceLinkHelper(docCollection: Workspace) {
if (!page) {
return;
}
const text = new page.Text([
const text = new Text([
{
insert: ' ',
attributes: {
@@ -5,7 +5,7 @@ import 'fake-indexeddb/auto';
import { AffineSchemas } from '@blocksuite/affine/blocks/schemas';
import { assertExists } from '@blocksuite/affine/global/utils';
import type { Blocks } from '@blocksuite/affine/store';
import { type Blocks, Text } from '@blocksuite/affine/store';
import { Schema } from '@blocksuite/store';
import { TestWorkspace } from '@blocksuite/store/test';
import { renderHook } from '@testing-library/react';
@@ -27,7 +27,7 @@ beforeEach(async () => {
expect(page).not.toBeNull();
assertExists(page);
const pageBlockId = page.addBlock('affine:page', {
title: new page.Text(''),
title: new Text(''),
});
const frameId = page.addBlock('affine:note', {}, pageBlockId);
page.addBlock('affine:paragraph', {}, frameId);
@@ -41,7 +41,7 @@ describe('useBlockSuitePagePreview', () => {
const id = page.addBlock(
'affine:paragraph',
{
text: new page.Text('Hello, world!'),
text: new Text('Hello, world!'),
},
page.getBlockByFlavour('affine:note')[0].id
);
@@ -58,7 +58,7 @@ describe('useBlockSuitePagePreview', () => {
page.addBlock(
'affine:paragraph',
{
text: new page.Text('First block!'),
text: new Text('First block!'),
},
page.getBlockByFlavour('affine:note')[0].id,
0
@@ -2,6 +2,7 @@ import { DebugLogger } from '@affine/debug';
import { Unreachable } from '@affine/env/constant';
import type { DocMode } from '@blocksuite/affine/blocks';
import type { DeltaInsert } from '@blocksuite/affine/inline';
import { Text } from '@blocksuite/affine/store';
import type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import { LiveData, ObjectPool, Service } from '@toeverything/infra';
import { omitBy } from 'lodash-es';
@@ -121,7 +122,7 @@ export class DocsService extends Service {
const { doc, release } = this.open(targetDocId);
doc.setPriorityLoad(10);
await doc.waitForSyncReady();
const text = new doc.blockSuiteDoc.Text([
const text = new Text([
{
insert: ' ',
attributes: {
@@ -0,0 +1,383 @@
import { type Disposable, Slot } from '@blocksuite/affine/global/utils';
import {
type AwarenessStore,
Blocks,
type BlockSuiteDoc,
type Doc,
type GetDocOptions,
type Query,
type Workspace,
type YBlock,
} from '@blocksuite/affine/store';
import { signal } from '@preact/signals-core';
import * as Y from 'yjs';
type DocOptions = {
id: string;
collection: Workspace;
doc: BlockSuiteDoc;
awarenessStore: AwarenessStore;
};
export class DocImpl implements Doc {
private _awarenessUpdateDisposable: Disposable | null = null;
private readonly _canRedo = signal(false);
private readonly _canUndo = signal(false);
private readonly _collection: Workspace;
private readonly _docMap = {
undefined: new Map<string, Blocks>(),
true: new Map<string, Blocks>(),
false: new Map<string, Blocks>(),
};
// doc/space container.
private readonly _handleYEvents = (events: Y.YEvent<YBlock | Y.Text>[]) => {
events.forEach(event => this._handleYEvent(event));
};
private _history!: Y.UndoManager;
private readonly _historyObserver = () => {
this._updateCanUndoRedoSignals();
this.slots.historyUpdated.emit();
};
private readonly _initSubDoc = () => {
let subDoc = this.rootDoc.spaces.get(this.id);
if (!subDoc) {
subDoc = new Y.Doc({
guid: this.id,
});
this.rootDoc.spaces.set(this.id, subDoc);
this._loaded = true;
this._onLoadSlot.emit();
} else {
this._loaded = false;
this.rootDoc.on('subdocs', this._onSubdocEvent);
}
return subDoc;
};
private _loaded!: boolean;
private readonly _onLoadSlot = new Slot();
private readonly _onSubdocEvent = ({
loaded,
}: {
loaded: Set<Y.Doc>;
}): void => {
const result = Array.from(loaded).find(
doc => doc.guid === this._ySpaceDoc.guid
);
if (!result) {
return;
}
this.rootDoc.off('subdocs', this._onSubdocEvent);
this._loaded = true;
this._onLoadSlot.emit();
};
/** Indicate whether the block tree is ready */
private _ready = false;
private _shouldTransact = true;
private readonly _updateCanUndoRedoSignals = () => {
const canRedo = this.readonly ? false : this._history.canRedo();
const canUndo = this.readonly ? false : this._history.canUndo();
if (this._canRedo.peek() !== canRedo) {
this._canRedo.value = canRedo;
}
if (this._canUndo.peek() !== canUndo) {
this._canUndo.value = canUndo;
}
};
protected readonly _yBlocks: Y.Map<YBlock>;
/**
* @internal Used for convenient access to the underlying Yjs map,
* can be used interchangeably with ySpace
*/
protected readonly _ySpaceDoc: Y.Doc;
readonly awarenessStore: AwarenessStore;
readonly id: string;
readonly rootDoc: BlockSuiteDoc;
readonly slots = {
historyUpdated: new Slot(),
yBlockUpdated: new Slot<
| {
type: 'add';
id: string;
}
| {
type: 'delete';
id: string;
}
>(),
};
get blobSync() {
return this.collection.blobSync;
}
get canRedo() {
return this._canRedo.peek();
}
get canUndo() {
return this._canUndo.peek();
}
get collection() {
return this._collection;
}
get docSync() {
return this.collection.docSync;
}
get history() {
return this._history;
}
get isEmpty() {
return this._yBlocks.size === 0;
}
get loaded() {
return this._loaded;
}
get meta() {
return this.collection.meta.getDocMeta(this.id);
}
get readonly(): boolean {
return this.awarenessStore.isReadonly(this);
}
get ready() {
return this._ready;
}
get schema() {
return this.collection.schema;
}
get spaceDoc() {
return this._ySpaceDoc;
}
get yBlocks() {
return this._yBlocks;
}
constructor({ id, collection, doc, awarenessStore }: DocOptions) {
this.id = id;
this.rootDoc = doc;
this.awarenessStore = awarenessStore;
this._ySpaceDoc = this._initSubDoc();
this._yBlocks = this._ySpaceDoc.getMap('blocks');
this._collection = collection;
}
private _getReadonlyKey(readonly?: boolean): 'true' | 'false' | 'undefined' {
return (readonly?.toString() as 'true' | 'false') ?? 'undefined';
}
private _handleVersion() {
// Initialization from empty yDoc, indicating that the document is new.
if (!this.collection.meta.hasVersion) {
this.collection.meta.writeVersion(this.collection);
}
}
private _handleYBlockAdd(id: string) {
this.slots.yBlockUpdated.emit({ type: 'add', id });
}
private _handleYBlockDelete(id: string) {
this.slots.yBlockUpdated.emit({ type: 'delete', id });
}
private _handleYEvent(event: Y.YEvent<YBlock | Y.Text | Y.Array<unknown>>) {
// event on top-level block store
if (event.target !== this._yBlocks) {
return;
}
event.keys.forEach((value, id) => {
try {
if (value.action === 'add') {
this._handleYBlockAdd(id);
return;
}
if (value.action === 'delete') {
this._handleYBlockDelete(id);
return;
}
} catch (e) {
console.error('An error occurred while handling Yjs event:');
console.error(e);
}
});
}
private _initYBlocks() {
const { _yBlocks } = this;
_yBlocks.observeDeep(this._handleYEvents);
this._history = new Y.UndoManager([_yBlocks], {
trackedOrigins: new Set([this._ySpaceDoc.clientID]),
});
this._history.on('stack-cleared', this._historyObserver);
this._history.on('stack-item-added', this._historyObserver);
this._history.on('stack-item-popped', this._historyObserver);
this._history.on('stack-item-updated', this._historyObserver);
}
/** Capture current operations to undo stack synchronously. */
captureSync() {
this._history.stopCapturing();
}
clear() {
this._yBlocks.clear();
}
clearQuery(query: Query, readonly?: boolean) {
const readonlyKey = this._getReadonlyKey(readonly);
this._docMap[readonlyKey].delete(JSON.stringify(query));
}
private _destroy() {
this._ySpaceDoc.destroy();
this._onLoadSlot.dispose();
this._loaded = false;
}
dispose() {
this.slots.historyUpdated.dispose();
this._awarenessUpdateDisposable?.dispose();
if (this.ready) {
this._yBlocks.unobserveDeep(this._handleYEvents);
this._yBlocks.clear();
}
}
getDoc({ readonly, query }: GetDocOptions = {}) {
const readonlyKey = this._getReadonlyKey(readonly);
const key = JSON.stringify(query);
if (this._docMap[readonlyKey].has(key)) {
return this._docMap[readonlyKey].get(key) as Blocks;
}
const doc = new Blocks({
blockCollection: this,
schema: this.collection.schema,
readonly,
query,
});
this._docMap[readonlyKey].set(key, doc);
return doc;
}
load(initFn?: () => void): this {
if (this.ready) {
return this;
}
this._ySpaceDoc.load();
if ((this.collection.meta.docs?.length ?? 0) <= 1) {
this._handleVersion();
}
this._initYBlocks();
this._yBlocks.forEach((_, id) => {
this._handleYBlockAdd(id);
});
this._awarenessUpdateDisposable = this.awarenessStore.slots.update.on(
() => {
// change readonly state will affect the undo/redo state
this._updateCanUndoRedoSignals();
}
);
initFn?.();
this._ready = true;
return this;
}
redo() {
if (this.readonly) {
console.error('cannot modify data in readonly mode');
return;
}
this._history.redo();
}
undo() {
if (this.readonly) {
console.error('cannot modify data in readonly mode');
return;
}
this._history.undo();
}
remove() {
this._destroy();
this.rootDoc.spaces.delete(this.id);
}
resetHistory() {
this._history.clear();
}
/**
* If `shouldTransact` is `false`, the transaction will not be push to the history stack.
*/
transact(fn: () => void, shouldTransact: boolean = this._shouldTransact) {
this._ySpaceDoc.transact(
() => {
try {
fn();
} catch (e) {
console.error(
`An error occurred while Y.doc ${this._ySpaceDoc.guid} transacting:`
);
console.error(e);
}
},
shouldTransact ? this.rootDoc.clientID : null
);
}
withoutTransact(callback: () => void) {
this._shouldTransact = false;
callback();
this._shouldTransact = true;
}
}
@@ -6,10 +6,10 @@ import type { BlockSuiteFlags } from '@blocksuite/affine/global/types';
import { NoopLogger, Slot } from '@blocksuite/affine/global/utils';
import {
AwarenessStore,
BlockCollection,
type Blocks,
BlockSuiteDoc,
type CreateDocOptions,
type Doc,
DocCollectionMeta,
type GetDocOptions,
type IdGenerator,
@@ -27,6 +27,8 @@ import {
} from '@blocksuite/affine/sync';
import { Awareness } from 'y-protocols/awareness.js';
import { DocImpl } from './doc';
type WorkspaceOptions = {
id?: string;
schema: Schema;
@@ -62,7 +64,7 @@ export class WorkspaceImpl implements Workspace {
readonly blobSync: BlobEngine;
readonly blockCollections = new Map<string, BlockCollection>();
readonly blockCollections = new Map<string, Doc>();
readonly doc: BlockSuiteDoc;
@@ -114,12 +116,11 @@ export class WorkspaceImpl implements Workspace {
private _bindDocMetaEvents() {
this.meta.docMetaAdded.on(docId => {
const doc = new BlockCollection({
const doc = new DocImpl({
id: docId,
collection: this,
doc: this.doc,
awarenessStore: this.awarenessStore,
idGenerator: this.idGenerator,
});
this.blockCollections.set(doc.id, doc);
});
@@ -185,8 +186,8 @@ export class WorkspaceImpl implements Workspace {
this.awarenessSync.disconnect();
}
getBlockCollection(docId: string): BlockCollection | null {
const space = this.docs.get(docId) as BlockCollection | undefined;
getBlockCollection(docId: string): Doc | null {
const space = this.docs.get(docId) as Doc | undefined;
return space ?? null;
}