mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-18 06:47:02 +08:00
feat: seperate createDoc and createStore (#11182)
This commit is contained in:
@@ -36,15 +36,16 @@ describe('editor host', () => {
|
||||
const collection = new TestWorkspace(createTestOptions());
|
||||
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
const store = doc.getStore({ extensions });
|
||||
doc.load();
|
||||
const rootId = doc.addBlock('test:page');
|
||||
const noteId = doc.addBlock('test:note', {}, rootId);
|
||||
const headingId = doc.addBlock('test:heading', { type: 'h1' }, noteId);
|
||||
const headingBlock = doc.getBlock(headingId)!;
|
||||
const rootId = store.addBlock('test:page');
|
||||
const noteId = store.addBlock('test:note', {}, rootId);
|
||||
const headingId = store.addBlock('test:heading', { type: 'h1' }, noteId);
|
||||
const headingBlock = store.getBlock(headingId)!;
|
||||
|
||||
const editorContainer = new TestEditorContainer();
|
||||
editorContainer.doc = doc;
|
||||
editorContainer.doc = store;
|
||||
editorContainer.specs = testSpecs;
|
||||
|
||||
document.body.append(editorContainer);
|
||||
|
||||
@@ -80,16 +80,16 @@ function createTestDoc(docId = defaultDocId) {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({
|
||||
id: docId,
|
||||
const doc = collection.createDoc(docId);
|
||||
doc.load();
|
||||
const store = doc.getStore({
|
||||
extensions: [
|
||||
pageSchemaExtension,
|
||||
tableSchemaExtension,
|
||||
flatTableSchemaExtension,
|
||||
],
|
||||
});
|
||||
doc.load();
|
||||
return doc;
|
||||
return store;
|
||||
}
|
||||
|
||||
test('init block without props should add default props', () => {
|
||||
|
||||
@@ -61,12 +61,12 @@ function createTestDoc(docId = defaultDocId) {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({
|
||||
id: docId,
|
||||
const doc = collection.createDoc(docId);
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
doc.load();
|
||||
return doc;
|
||||
return store;
|
||||
}
|
||||
|
||||
function requestIdleCallbackPolyfill(
|
||||
@@ -97,7 +97,7 @@ describe('basic', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc = collection.createDoc({ id: 'doc:home' });
|
||||
const doc = collection.createDoc('doc:home');
|
||||
doc.load();
|
||||
const actual = serializCollection(collection.doc);
|
||||
const actualDoc = actual[spaceMetaId].pages[0] as DocMeta;
|
||||
@@ -148,23 +148,23 @@ describe('basic', () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({
|
||||
id: 'space:0',
|
||||
const doc = collection.createDoc('space:0');
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
|
||||
const readyCallback = vi.fn();
|
||||
const rootAddedCallback = vi.fn();
|
||||
doc.slots.ready.subscribe(readyCallback);
|
||||
doc.slots.rootAdded.subscribe(rootAddedCallback);
|
||||
store.slots.ready.subscribe(readyCallback);
|
||||
store.slots.rootAdded.subscribe(rootAddedCallback);
|
||||
|
||||
doc.load(() => {
|
||||
const rootId = doc.addBlock('affine:page', {
|
||||
store.load(() => {
|
||||
const rootId = store.addBlock('affine:page', {
|
||||
title: new Text(),
|
||||
});
|
||||
expect(rootAddedCallback).toBeCalledTimes(1);
|
||||
|
||||
doc.addBlock('affine:note', {}, rootId);
|
||||
store.addBlock('affine:note', {}, rootId);
|
||||
});
|
||||
|
||||
expect(readyCallback).toBeCalledTimes(1);
|
||||
@@ -175,12 +175,12 @@ describe('basic', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const collection2 = new TestWorkspace(options);
|
||||
const doc = collection.createDoc({
|
||||
id: 'space:0',
|
||||
const doc = collection.createDoc('space:0');
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
doc.load(() => {
|
||||
doc.addBlock('affine:page', {
|
||||
store.addBlock('affine:page', {
|
||||
title: new Text(),
|
||||
});
|
||||
});
|
||||
@@ -206,9 +206,7 @@ describe('basic', () => {
|
||||
// apply doc update
|
||||
const update = encodeStateAsUpdate(doc.spaceDoc);
|
||||
expect(collection2.docs.size).toBe(1);
|
||||
const doc2 = collection2.getDoc('space:0', {
|
||||
extensions,
|
||||
});
|
||||
const doc2 = collection2.getDoc('space:0');
|
||||
if (!doc2) {
|
||||
throw new Error('doc2 is not found');
|
||||
}
|
||||
@@ -382,12 +380,15 @@ describe('addBlock', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc0 = collection.createDoc({ id: 'doc:home' });
|
||||
const doc1 = collection.createDoc({ id: 'space:doc1' });
|
||||
const doc0 = collection.createDoc('doc:home');
|
||||
const doc1 = collection.createDoc('space:doc1');
|
||||
await Promise.all([doc0.load(), doc1.load()]);
|
||||
assert.equal(collection.docs.size, 2);
|
||||
const store0 = doc0.getStore({
|
||||
extensions,
|
||||
});
|
||||
|
||||
doc0.addBlock('affine:page', {
|
||||
store0.addBlock('affine:page', {
|
||||
title: new Text(),
|
||||
});
|
||||
collection.removeDoc(doc0.id);
|
||||
@@ -407,8 +408,7 @@ describe('addBlock', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc0 = collection.createDoc({ id: 'doc:home' });
|
||||
|
||||
const doc0 = collection.createDoc('doc:home');
|
||||
collection.removeDoc(doc0.id);
|
||||
assert.equal(collection.docs.size, 0);
|
||||
});
|
||||
@@ -417,7 +417,7 @@ describe('addBlock', () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
collection.createDoc({ id: 'doc:home' });
|
||||
collection.createDoc('doc:home');
|
||||
|
||||
assert.deepEqual(
|
||||
collection.meta.docMetas.map(({ id, title }) => ({
|
||||
|
||||
@@ -31,12 +31,15 @@ test('trigger props updated', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
doc.load();
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
|
||||
doc.addBlock('affine:page');
|
||||
store.addBlock('affine:page');
|
||||
|
||||
const rootModel = doc.root as RootBlockModel;
|
||||
const rootModel = store.root as RootBlockModel;
|
||||
|
||||
expect(rootModel).not.toBeNull();
|
||||
|
||||
@@ -91,12 +94,15 @@ test('stash and pop', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
doc.load();
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
|
||||
doc.addBlock('affine:page');
|
||||
store.addBlock('affine:page');
|
||||
|
||||
const rootModel = doc.root as RootBlockModel;
|
||||
const rootModel = store.root as RootBlockModel;
|
||||
|
||||
expect(rootModel).not.toBeNull();
|
||||
|
||||
@@ -161,12 +167,15 @@ test('always get latest value in onChange', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
doc.load();
|
||||
const store = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
|
||||
doc.addBlock('affine:page');
|
||||
store.addBlock('affine:page');
|
||||
|
||||
const rootModel = doc.root as RootBlockModel;
|
||||
const rootModel = store.root as RootBlockModel;
|
||||
|
||||
expect(rootModel).not.toBeNull();
|
||||
|
||||
@@ -207,11 +216,15 @@ test('query', () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc1 = collection.createDoc({ id: 'home', extensions });
|
||||
doc1.load();
|
||||
const doc2 = collection.getDoc('home', { extensions });
|
||||
|
||||
const doc3 = collection.getDoc('home', {
|
||||
const doc = collection.createDoc('home');
|
||||
doc.load();
|
||||
const store1 = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
const store2 = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
const store3 = doc.getStore({
|
||||
extensions,
|
||||
query: {
|
||||
mode: 'loose',
|
||||
@@ -223,48 +236,53 @@ test('query', () => {
|
||||
],
|
||||
},
|
||||
});
|
||||
expect(doc1).toBe(doc2);
|
||||
expect(doc1).not.toBe(doc3);
|
||||
expect(store1).toBe(store2);
|
||||
expect(store1).not.toBe(store3);
|
||||
|
||||
const page = doc1.addBlock('affine:page');
|
||||
const note = doc1.addBlock('affine:note', {}, page);
|
||||
const paragraph1 = doc1.addBlock('affine:paragraph', {}, note);
|
||||
const list1 = doc1.addBlock('affine:list' as never, {}, note);
|
||||
const page = store1.addBlock('affine:page');
|
||||
const note = store1.addBlock('affine:note', {}, page);
|
||||
const paragraph1 = store1.addBlock('affine:paragraph', {}, note);
|
||||
const list1 = store1.addBlock('affine:list' as never, {}, note);
|
||||
|
||||
expect(doc2?.getBlock(paragraph1)?.blockViewType).toBe('display');
|
||||
expect(doc2?.getBlock(list1)?.blockViewType).toBe('display');
|
||||
expect(doc3?.getBlock(list1)?.blockViewType).toBe('hidden');
|
||||
expect(store2?.getBlock(paragraph1)?.blockViewType).toBe('display');
|
||||
expect(store2?.getBlock(list1)?.blockViewType).toBe('display');
|
||||
expect(store3?.getBlock(list1)?.blockViewType).toBe('hidden');
|
||||
|
||||
const list2 = doc1.addBlock('affine:list' as never, {}, note);
|
||||
const list2 = store1.addBlock('affine:list' as never, {}, note);
|
||||
|
||||
expect(doc2?.getBlock(list2)?.blockViewType).toBe('display');
|
||||
expect(doc3?.getBlock(list2)?.blockViewType).toBe('hidden');
|
||||
expect(store2?.getBlock(list2)?.blockViewType).toBe('display');
|
||||
expect(store3?.getBlock(list2)?.blockViewType).toBe('hidden');
|
||||
});
|
||||
|
||||
test('local readonly', () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc1 = collection.createDoc({ id: 'home', extensions });
|
||||
doc1.load();
|
||||
const doc2 = collection.getDoc('home', { readonly: true, extensions });
|
||||
const doc3 = collection.getDoc('home', { readonly: false, extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
const store1 = doc.getStore({
|
||||
extensions,
|
||||
});
|
||||
const store2 = doc.getStore({
|
||||
readonly: true,
|
||||
extensions,
|
||||
});
|
||||
const store3 = doc.getStore({ readonly: false, extensions });
|
||||
|
||||
expect(doc1.readonly).toBeFalsy();
|
||||
expect(doc2?.readonly).toBeTruthy();
|
||||
expect(doc3?.readonly).toBeFalsy();
|
||||
expect(store1.readonly).toBeFalsy();
|
||||
expect(store2.readonly).toBeTruthy();
|
||||
expect(store3.readonly).toBeFalsy();
|
||||
|
||||
doc1.readonly = true;
|
||||
store1.readonly = true;
|
||||
|
||||
expect(doc1.readonly).toBeTruthy();
|
||||
expect(doc2?.readonly).toBeTruthy();
|
||||
expect(doc3?.readonly).toBeFalsy();
|
||||
expect(store1.readonly).toBeTruthy();
|
||||
expect(store2.readonly).toBeTruthy();
|
||||
expect(store3.readonly).toBeFalsy();
|
||||
|
||||
doc1.readonly = false;
|
||||
store1.readonly = false;
|
||||
|
||||
expect(doc1.readonly).toBeFalsy();
|
||||
expect(doc2?.readonly).toBeTruthy();
|
||||
expect(doc3?.readonly).toBeFalsy();
|
||||
expect(store1.readonly).toBeFalsy();
|
||||
expect(store2.readonly).toBeTruthy();
|
||||
expect(store3.readonly).toBeFalsy();
|
||||
});
|
||||
|
||||
describe('move blocks', () => {
|
||||
@@ -274,21 +292,22 @@ describe('move blocks', () => {
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
doc.load();
|
||||
const pageId = doc.addBlock('affine:page');
|
||||
const page = doc.getBlock(pageId)!.model;
|
||||
const store = doc.getStore({ extensions });
|
||||
const pageId = store.addBlock('affine:page');
|
||||
const page = store.getBlock(pageId)!.model;
|
||||
|
||||
const noteIds = doc.addBlocks(
|
||||
const noteIds = store.addBlocks(
|
||||
[1, 2, 3].map(i => ({
|
||||
flavour: 'affine:note',
|
||||
blockProps: { id: `${i}` },
|
||||
})),
|
||||
page
|
||||
);
|
||||
const notes = noteIds.map(id => doc.getBlock(id)!.model);
|
||||
const notes = noteIds.map(id => store.getBlock(id)!.model);
|
||||
|
||||
context.doc = doc;
|
||||
context.doc = store;
|
||||
context.page = page;
|
||||
context.notes = notes;
|
||||
});
|
||||
|
||||
@@ -99,9 +99,10 @@ function createTestDoc(docId = defaultDocId) {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: docId, extensions });
|
||||
const doc = collection.createDoc(docId);
|
||||
doc.load();
|
||||
return doc;
|
||||
const store = doc.getStore({ extensions });
|
||||
return store;
|
||||
}
|
||||
|
||||
describe('schema', () => {
|
||||
|
||||
@@ -59,10 +59,11 @@ test('model to snapshot', () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
const store = doc.getStore({ extensions });
|
||||
doc.load();
|
||||
doc.addBlock('page');
|
||||
const rootModel = doc.root as RootBlockModel;
|
||||
store.addBlock('page');
|
||||
const rootModel = store.root as RootBlockModel;
|
||||
|
||||
expect(rootModel).not.toBeNull();
|
||||
const snapshot = transformer.toSnapshot({
|
||||
@@ -76,10 +77,11 @@ test('snapshot to model', async () => {
|
||||
const options = createTestOptions();
|
||||
const collection = new TestWorkspace(options);
|
||||
collection.meta.initialize();
|
||||
const doc = collection.createDoc({ id: 'home', extensions });
|
||||
const doc = collection.createDoc('home');
|
||||
const store = doc.getStore({ extensions });
|
||||
doc.load();
|
||||
doc.addBlock('page');
|
||||
const rootModel = doc.root as RootBlockModel;
|
||||
store.addBlock('page');
|
||||
const rootModel = store.root as RootBlockModel;
|
||||
|
||||
const tempDoc = new Y.Doc();
|
||||
const map = tempDoc.getMap('temp');
|
||||
|
||||
@@ -1127,8 +1127,9 @@ export class Store {
|
||||
schema: this.schema,
|
||||
blobCRUD: this.workspace.blobSync,
|
||||
docCRUD: {
|
||||
create: (id: string) => this.workspace.createDoc({ id }),
|
||||
get: (id: string) => this.workspace.getDoc(id),
|
||||
create: (id: string) => this.workspace.createDoc(id).getStore({ id }),
|
||||
get: (id: string) =>
|
||||
this.workspace.getDoc(id)?.getStore({ id }) ?? null,
|
||||
delete: (id: string) => this.workspace.removeDoc(id),
|
||||
},
|
||||
middlewares,
|
||||
|
||||
@@ -4,8 +4,7 @@ import type { Awareness } from 'y-protocols/awareness.js';
|
||||
import type * as Y from 'yjs';
|
||||
|
||||
import type { IdGenerator } from '../utils/id-generator.js';
|
||||
import type { CreateBlocksOptions, Doc, GetBlocksOptions } from './doc.js';
|
||||
import type { Store } from './store/store.js';
|
||||
import type { Doc } from './doc.js';
|
||||
import type { WorkspaceMeta } from './workspace-meta.js';
|
||||
|
||||
export interface Workspace {
|
||||
@@ -25,8 +24,8 @@ export interface Workspace {
|
||||
docRemoved: Subject<string>;
|
||||
};
|
||||
|
||||
createDoc(options?: CreateBlocksOptions): Store;
|
||||
getDoc(docId: string, options?: GetBlocksOptions): Store | null;
|
||||
createDoc(docId?: string): Doc;
|
||||
getDoc(docId: string): Doc | null;
|
||||
removeDoc(docId: string): void;
|
||||
|
||||
dispose(): void;
|
||||
|
||||
@@ -15,13 +15,7 @@ import { Awareness } from 'y-protocols/awareness.js';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import type { ExtensionType } from '../extension/extension.js';
|
||||
import type {
|
||||
CreateBlocksOptions,
|
||||
GetBlocksOptions,
|
||||
Store,
|
||||
Workspace,
|
||||
WorkspaceMeta,
|
||||
} from '../model/index.js';
|
||||
import type { Doc, Workspace, WorkspaceMeta } from '../model/index.js';
|
||||
import { type IdGenerator, nanoid } from '../utils/id-generator.js';
|
||||
import { AwarenessStore } from '../yjs/index.js';
|
||||
import { TestDoc } from './test-doc.js';
|
||||
@@ -155,14 +149,9 @@ export class TestWorkspace implements Workspace {
|
||||
* If the `init` parameter is passed, a `surface`, `note`, and `paragraph` block
|
||||
* will be created in the doc simultaneously.
|
||||
*/
|
||||
createDoc(options: CreateBlocksOptions = {}) {
|
||||
const {
|
||||
id: docId = this.idGenerator(),
|
||||
query,
|
||||
readonly,
|
||||
extensions,
|
||||
} = options;
|
||||
if (this._hasDoc(docId)) {
|
||||
createDoc(docId?: string): Doc {
|
||||
const id = docId ?? this.idGenerator();
|
||||
if (this._hasDoc(id)) {
|
||||
throw new BlockSuiteError(
|
||||
ErrorCode.DocCollectionError,
|
||||
'doc already exists'
|
||||
@@ -170,18 +159,13 @@ export class TestWorkspace implements Workspace {
|
||||
}
|
||||
|
||||
this.meta.addDocMeta({
|
||||
id: docId,
|
||||
id,
|
||||
title: '',
|
||||
createDate: Date.now(),
|
||||
tags: [],
|
||||
});
|
||||
this.slots.docCreated.next(docId);
|
||||
return this.getDoc(docId, {
|
||||
id: docId,
|
||||
query,
|
||||
readonly,
|
||||
extensions,
|
||||
}) as Store;
|
||||
this.slots.docCreated.next(id);
|
||||
return this.getDoc(id) as Doc;
|
||||
}
|
||||
|
||||
dispose() {
|
||||
@@ -203,12 +187,9 @@ export class TestWorkspace implements Workspace {
|
||||
return space ?? null;
|
||||
}
|
||||
|
||||
getDoc(
|
||||
docId: string,
|
||||
options: GetBlocksOptions = { id: docId }
|
||||
): Store | null {
|
||||
getDoc(docId: string): Doc | null {
|
||||
const collection = this.getBlockCollection(docId);
|
||||
return collection?.getStore(options) ?? null;
|
||||
return collection;
|
||||
}
|
||||
|
||||
removeDoc(docId: string) {
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { z } from 'zod';
|
||||
|
||||
import type { Store } from '../model/store/store';
|
||||
import type { Store } from '../model';
|
||||
import type { DocMeta, DocsPropertiesMeta } from '../model/workspace-meta';
|
||||
|
||||
export type BlockSnapshot = {
|
||||
|
||||
Reference in New Issue
Block a user