mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-07-21 03:56:23 +08:00
refactor(editor): move workspace meta to affine (#9524)
This commit is contained in:
@@ -1,3 +1,2 @@
|
||||
export * from './doc/index.js';
|
||||
export * from './meta.js';
|
||||
export * from './workspace.js';
|
||||
|
||||
@@ -54,13 +54,12 @@ export interface WorkspaceMeta {
|
||||
get name(): string | undefined;
|
||||
setName(name: string): void;
|
||||
|
||||
commonFieldsUpdated: Slot;
|
||||
|
||||
hasVersion: boolean;
|
||||
writeVersion(workspace: Workspace): void;
|
||||
get docs(): unknown[] | undefined;
|
||||
initialize(): void;
|
||||
|
||||
commonFieldsUpdated: Slot;
|
||||
docMetaAdded: Slot<string>;
|
||||
docMetaRemoved: Slot<string>;
|
||||
docMetaUpdated: Slot;
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
export { createAutoIncrementIdGenerator } from '../utils/id-generator.js';
|
||||
export * from './test-doc.js';
|
||||
export * from './test-meta.js';
|
||||
export * from './test-workspace.js';
|
||||
|
||||
@@ -0,0 +1,262 @@
|
||||
import { Slot } from '@blocksuite/global/utils';
|
||||
import type * as Y from 'yjs';
|
||||
|
||||
import { COLLECTION_VERSION, PAGE_VERSION } from '../consts.js';
|
||||
import { createYProxy } from '../reactive/proxy.js';
|
||||
import type {
|
||||
DocMeta,
|
||||
DocsPropertiesMeta,
|
||||
Workspace,
|
||||
WorkspaceMeta,
|
||||
} from '../store/workspace.js';
|
||||
|
||||
export type DocCollectionMetaState = {
|
||||
pages?: unknown[];
|
||||
properties?: DocsPropertiesMeta;
|
||||
workspaceVersion?: number;
|
||||
pageVersion?: number;
|
||||
blockVersions?: Record<string, number>;
|
||||
name?: string;
|
||||
avatar?: string;
|
||||
};
|
||||
|
||||
export class TestMeta implements WorkspaceMeta {
|
||||
private readonly _handleDocCollectionMetaEvents = (
|
||||
events: Y.YEvent<Y.Array<unknown> | Y.Text | Y.Map<unknown>>[]
|
||||
) => {
|
||||
events.forEach(e => {
|
||||
const hasKey = (k: string) =>
|
||||
e.target === this._yMap && e.changes.keys.has(k);
|
||||
|
||||
if (
|
||||
e.target === this.yDocs ||
|
||||
e.target.parent === this.yDocs ||
|
||||
hasKey('pages')
|
||||
) {
|
||||
this._handleDocMetaEvent();
|
||||
}
|
||||
|
||||
if (hasKey('name') || hasKey('avatar')) {
|
||||
this._handleCommonFieldsEvent();
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
private _prevDocs = new Set<string>();
|
||||
|
||||
protected readonly _proxy: DocCollectionMetaState;
|
||||
|
||||
protected readonly _yMap: Y.Map<
|
||||
DocCollectionMetaState[keyof DocCollectionMetaState]
|
||||
>;
|
||||
|
||||
commonFieldsUpdated = new Slot();
|
||||
|
||||
readonly doc: Y.Doc;
|
||||
|
||||
docMetaAdded = new Slot<string>();
|
||||
|
||||
docMetaRemoved = new Slot<string>();
|
||||
|
||||
docMetaUpdated = new Slot();
|
||||
|
||||
readonly id: string = 'meta';
|
||||
|
||||
get avatar() {
|
||||
return this._proxy.avatar;
|
||||
}
|
||||
|
||||
get blockVersions() {
|
||||
return this._proxy.blockVersions;
|
||||
}
|
||||
|
||||
get docMetas() {
|
||||
if (!this._proxy.pages) {
|
||||
return [] as DocMeta[];
|
||||
}
|
||||
return this._proxy.pages as DocMeta[];
|
||||
}
|
||||
|
||||
get docs() {
|
||||
return this._proxy.pages;
|
||||
}
|
||||
|
||||
get hasVersion() {
|
||||
if (!this.blockVersions || !this.pageVersion || !this.workspaceVersion) {
|
||||
return false;
|
||||
}
|
||||
return Object.keys(this.blockVersions).length > 0;
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._proxy.name;
|
||||
}
|
||||
|
||||
get pageVersion() {
|
||||
return this._proxy.pageVersion;
|
||||
}
|
||||
|
||||
get properties(): DocsPropertiesMeta {
|
||||
const meta = this._proxy.properties;
|
||||
if (!meta) {
|
||||
return {
|
||||
tags: {
|
||||
options: [],
|
||||
},
|
||||
};
|
||||
}
|
||||
return meta;
|
||||
}
|
||||
|
||||
get workspaceVersion() {
|
||||
return this._proxy.workspaceVersion;
|
||||
}
|
||||
|
||||
get yDocs() {
|
||||
return this._yMap.get('pages') as unknown as Y.Array<unknown>;
|
||||
}
|
||||
|
||||
constructor(doc: Y.Doc) {
|
||||
this.doc = doc;
|
||||
const map = doc.getMap(this.id) as Y.Map<
|
||||
DocCollectionMetaState[keyof DocCollectionMetaState]
|
||||
>;
|
||||
this._yMap = map;
|
||||
this._proxy = createYProxy(map);
|
||||
this._yMap.observeDeep(this._handleDocCollectionMetaEvents);
|
||||
}
|
||||
|
||||
private _handleCommonFieldsEvent() {
|
||||
this.commonFieldsUpdated.emit();
|
||||
}
|
||||
|
||||
private _handleDocMetaEvent() {
|
||||
const { docMetas, _prevDocs } = this;
|
||||
|
||||
const newDocs = new Set<string>();
|
||||
|
||||
docMetas.forEach(docMeta => {
|
||||
if (!_prevDocs.has(docMeta.id)) {
|
||||
this.docMetaAdded.emit(docMeta.id);
|
||||
}
|
||||
newDocs.add(docMeta.id);
|
||||
});
|
||||
|
||||
_prevDocs.forEach(prevDocId => {
|
||||
const isRemoved = newDocs.has(prevDocId) === false;
|
||||
if (isRemoved) {
|
||||
this.docMetaRemoved.emit(prevDocId);
|
||||
}
|
||||
});
|
||||
|
||||
this._prevDocs = newDocs;
|
||||
|
||||
this.docMetaUpdated.emit();
|
||||
}
|
||||
|
||||
addDocMeta(doc: DocMeta, index?: number) {
|
||||
this.doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
const docs = this.docs as unknown[];
|
||||
if (index === undefined) {
|
||||
docs.push(doc);
|
||||
} else {
|
||||
docs.splice(index, 0, doc);
|
||||
}
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
getDocMeta(id: string) {
|
||||
return this.docMetas.find(doc => doc.id === id);
|
||||
}
|
||||
|
||||
initialize() {
|
||||
if (!this._proxy.pages) {
|
||||
this._proxy.pages = [];
|
||||
}
|
||||
}
|
||||
|
||||
removeDocMeta(id: string) {
|
||||
// you cannot delete a doc if there's no doc
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
|
||||
const docMeta = this.docMetas;
|
||||
const index = docMeta.findIndex((doc: DocMeta) => id === doc.id);
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
this.doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
this.docs.splice(index, 1);
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setAvatar(avatar: string) {
|
||||
this.doc.transact(() => {
|
||||
this._proxy.avatar = avatar;
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setDocMeta(id: string, props: Partial<DocMeta>) {
|
||||
const docs = (this.docs as DocMeta[]) ?? [];
|
||||
const index = docs.findIndex((doc: DocMeta) => id === doc.id);
|
||||
|
||||
this.doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
if (index === -1) return;
|
||||
|
||||
const doc = this.docs[index] as Record<string, unknown>;
|
||||
Object.entries(props).forEach(([key, value]) => {
|
||||
doc[key] = value;
|
||||
});
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.doc.transact(() => {
|
||||
this._proxy.name = name;
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setProperties(meta: DocsPropertiesMeta) {
|
||||
this._proxy.properties = meta;
|
||||
this.docMetaUpdated.emit();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal Only for doc initialization
|
||||
*/
|
||||
writeVersion(collection: Workspace) {
|
||||
const { blockVersions, pageVersion, workspaceVersion } = this._proxy;
|
||||
|
||||
if (!workspaceVersion) {
|
||||
this._proxy.workspaceVersion = COLLECTION_VERSION;
|
||||
} else {
|
||||
console.error('Workspace version is already set');
|
||||
}
|
||||
|
||||
if (!pageVersion) {
|
||||
this._proxy.pageVersion = PAGE_VERSION;
|
||||
} else {
|
||||
console.error('Doc version is already set');
|
||||
}
|
||||
|
||||
if (!blockVersions) {
|
||||
const _versions: Record<string, number> = {};
|
||||
collection.schema.flavourSchemaMap.forEach((schema, flavour) => {
|
||||
_versions[flavour] = schema.version;
|
||||
});
|
||||
this._proxy.blockVersions = _versions;
|
||||
} else {
|
||||
console.error('Block versions is already set');
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,6 @@ import type { Schema } from '../schema/index.js';
|
||||
import {
|
||||
type Blocks,
|
||||
type CreateBlocksOptions,
|
||||
DocCollectionMeta,
|
||||
type GetBlocksOptions,
|
||||
type Workspace,
|
||||
type WorkspaceMeta,
|
||||
@@ -28,6 +27,7 @@ import {
|
||||
import { type IdGenerator, nanoid } from '../utils/id-generator.js';
|
||||
import { AwarenessStore, type RawAwarenessState } from '../yjs/index.js';
|
||||
import { TestDoc } from './test-doc.js';
|
||||
import { TestMeta } from './test-meta.js';
|
||||
|
||||
export type DocCollectionOptions = {
|
||||
schema: Schema;
|
||||
@@ -146,7 +146,7 @@ export class TestWorkspace implements Workspace {
|
||||
|
||||
this.idGenerator = idGenerator ?? nanoid;
|
||||
|
||||
this.meta = new DocCollectionMeta(this.doc);
|
||||
this.meta = new TestMeta(this.doc);
|
||||
this._bindDocMetaEvents();
|
||||
}
|
||||
|
||||
|
||||
+69
-79
@@ -1,16 +1,17 @@
|
||||
import { Slot } from '@blocksuite/global/utils';
|
||||
import { Slot } from '@blocksuite/affine/global/utils';
|
||||
import {
|
||||
createYProxy,
|
||||
type DocMeta,
|
||||
type DocsPropertiesMeta,
|
||||
type Workspace,
|
||||
type WorkspaceMeta,
|
||||
} from '@blocksuite/affine/store';
|
||||
import type * as Y from 'yjs';
|
||||
|
||||
import { COLLECTION_VERSION, PAGE_VERSION } from '../consts.js';
|
||||
import { createYProxy } from '../reactive/proxy.js';
|
||||
import type {
|
||||
DocMeta,
|
||||
DocsPropertiesMeta,
|
||||
Workspace,
|
||||
WorkspaceMeta,
|
||||
} from './workspace.js';
|
||||
const COLLECTION_VERSION = 2;
|
||||
const PAGE_VERSION = 2;
|
||||
|
||||
export type DocCollectionMetaState = {
|
||||
type MetaState = {
|
||||
pages?: unknown[];
|
||||
properties?: DocsPropertiesMeta;
|
||||
workspaceVersion?: number;
|
||||
@@ -20,7 +21,12 @@ export type DocCollectionMetaState = {
|
||||
avatar?: string;
|
||||
};
|
||||
|
||||
export class DocCollectionMeta implements WorkspaceMeta {
|
||||
export class WorkspaceMetaImpl implements WorkspaceMeta {
|
||||
commonFieldsUpdated = new Slot();
|
||||
docMetaAdded = new Slot<string>();
|
||||
docMetaRemoved = new Slot<string>();
|
||||
docMetaUpdated = new Slot();
|
||||
|
||||
private readonly _handleDocCollectionMetaEvents = (
|
||||
events: Y.YEvent<Y.Array<unknown> | Y.Text | Y.Map<unknown>>[]
|
||||
) => {
|
||||
@@ -42,58 +48,30 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
});
|
||||
};
|
||||
|
||||
private readonly _id: string = 'meta';
|
||||
private readonly _doc: Y.Doc;
|
||||
private readonly _proxy: MetaState;
|
||||
private readonly _yMap: Y.Map<MetaState[keyof MetaState]>;
|
||||
private _prevDocs = new Set<string>();
|
||||
|
||||
protected readonly _proxy: DocCollectionMetaState;
|
||||
|
||||
protected readonly _yMap: Y.Map<
|
||||
DocCollectionMetaState[keyof DocCollectionMetaState]
|
||||
>;
|
||||
|
||||
commonFieldsUpdated = new Slot();
|
||||
|
||||
readonly doc: Y.Doc;
|
||||
|
||||
docMetaAdded = new Slot<string>();
|
||||
|
||||
docMetaRemoved = new Slot<string>();
|
||||
|
||||
docMetaUpdated = new Slot();
|
||||
|
||||
readonly id: string = 'meta';
|
||||
|
||||
get avatar() {
|
||||
return this._proxy.avatar;
|
||||
}
|
||||
|
||||
get blockVersions() {
|
||||
return this._proxy.blockVersions;
|
||||
}
|
||||
|
||||
get docMetas() {
|
||||
if (!this._proxy.pages) {
|
||||
return [] as DocMeta[];
|
||||
}
|
||||
return this._proxy.pages as DocMeta[];
|
||||
}
|
||||
|
||||
get docs() {
|
||||
return this._proxy.pages;
|
||||
}
|
||||
|
||||
get hasVersion() {
|
||||
if (!this.blockVersions || !this.pageVersion || !this.workspaceVersion) {
|
||||
return false;
|
||||
}
|
||||
return Object.keys(this.blockVersions).length > 0;
|
||||
setAvatar(avatar: string) {
|
||||
this._doc.transact(() => {
|
||||
this._proxy.avatar = avatar;
|
||||
}, this._doc.clientID);
|
||||
}
|
||||
|
||||
get name() {
|
||||
return this._proxy.name;
|
||||
}
|
||||
|
||||
get pageVersion() {
|
||||
return this._proxy.pageVersion;
|
||||
setName(name: string) {
|
||||
this._doc.transact(() => {
|
||||
this._proxy.name = name;
|
||||
}, this._doc.clientID);
|
||||
}
|
||||
|
||||
get properties(): DocsPropertiesMeta {
|
||||
@@ -108,7 +86,38 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
return meta;
|
||||
}
|
||||
|
||||
get workspaceVersion() {
|
||||
setProperties(meta: DocsPropertiesMeta) {
|
||||
this._proxy.properties = meta;
|
||||
this.docMetaUpdated.emit();
|
||||
}
|
||||
|
||||
get docMetas() {
|
||||
if (!this._proxy.pages) {
|
||||
return [] as DocMeta[];
|
||||
}
|
||||
return this._proxy.pages as DocMeta[];
|
||||
}
|
||||
|
||||
get docs() {
|
||||
return this._proxy.pages;
|
||||
}
|
||||
|
||||
get hasVersion() {
|
||||
if (!this._blockVersions || !this._pageVersion || !this._workspaceVersion) {
|
||||
return false;
|
||||
}
|
||||
return Object.keys(this._blockVersions).length > 0;
|
||||
}
|
||||
|
||||
private get _blockVersions() {
|
||||
return this._proxy.blockVersions;
|
||||
}
|
||||
|
||||
private get _pageVersion() {
|
||||
return this._proxy.pageVersion;
|
||||
}
|
||||
|
||||
private get _workspaceVersion() {
|
||||
return this._proxy.workspaceVersion;
|
||||
}
|
||||
|
||||
@@ -117,10 +126,8 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
}
|
||||
|
||||
constructor(doc: Y.Doc) {
|
||||
this.doc = doc;
|
||||
const map = doc.getMap(this.id) as Y.Map<
|
||||
DocCollectionMetaState[keyof DocCollectionMetaState]
|
||||
>;
|
||||
this._doc = doc;
|
||||
const map = doc.getMap(this._id) as Y.Map<MetaState[keyof MetaState]>;
|
||||
this._yMap = map;
|
||||
this._proxy = createYProxy(map);
|
||||
this._yMap.observeDeep(this._handleDocCollectionMetaEvents);
|
||||
@@ -155,7 +162,7 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
}
|
||||
|
||||
addDocMeta(doc: DocMeta, index?: number) {
|
||||
this.doc.transact(() => {
|
||||
this._doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
@@ -165,7 +172,7 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
} else {
|
||||
docs.splice(index, 0, doc);
|
||||
}
|
||||
}, this.doc.clientID);
|
||||
}, this._doc.clientID);
|
||||
}
|
||||
|
||||
getDocMeta(id: string) {
|
||||
@@ -189,25 +196,19 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
if (index === -1) {
|
||||
return;
|
||||
}
|
||||
this.doc.transact(() => {
|
||||
this._doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
this.docs.splice(index, 1);
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setAvatar(avatar: string) {
|
||||
this.doc.transact(() => {
|
||||
this._proxy.avatar = avatar;
|
||||
}, this.doc.clientID);
|
||||
}, this._doc.clientID);
|
||||
}
|
||||
|
||||
setDocMeta(id: string, props: Partial<DocMeta>) {
|
||||
const docs = (this.docs as DocMeta[]) ?? [];
|
||||
const index = docs.findIndex((doc: DocMeta) => id === doc.id);
|
||||
|
||||
this.doc.transact(() => {
|
||||
this._doc.transact(() => {
|
||||
if (!this.docs) {
|
||||
return;
|
||||
}
|
||||
@@ -217,18 +218,7 @@ export class DocCollectionMeta implements WorkspaceMeta {
|
||||
Object.entries(props).forEach(([key, value]) => {
|
||||
doc[key] = value;
|
||||
});
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setName(name: string) {
|
||||
this.doc.transact(() => {
|
||||
this._proxy.name = name;
|
||||
}, this.doc.clientID);
|
||||
}
|
||||
|
||||
setProperties(meta: DocsPropertiesMeta) {
|
||||
this._proxy.properties = meta;
|
||||
this.docMetaUpdated.emit();
|
||||
}, this._doc.clientID);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -9,7 +9,6 @@ import {
|
||||
type Blocks,
|
||||
type CreateBlocksOptions,
|
||||
type Doc,
|
||||
DocCollectionMeta,
|
||||
type GetBlocksOptions,
|
||||
type IdGenerator,
|
||||
nanoid,
|
||||
@@ -29,6 +28,7 @@ import { Awareness } from 'y-protocols/awareness.js';
|
||||
import * as Y from 'yjs';
|
||||
|
||||
import { DocImpl } from './doc';
|
||||
import { WorkspaceMetaImpl } from './meta';
|
||||
|
||||
type WorkspaceOptions = {
|
||||
id?: string;
|
||||
@@ -111,7 +111,7 @@ export class WorkspaceImpl implements Workspace {
|
||||
|
||||
this.idGenerator = nanoid;
|
||||
|
||||
this.meta = new DocCollectionMeta(this.doc);
|
||||
this.meta = new WorkspaceMetaImpl(this.doc);
|
||||
this._bindDocMetaEvents();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user