Files
AFFiNE-Mirror/blocksuite/framework/store/src/test/test-meta.ts
T
EYHN 2d1600fa00 refactor(core): implement doc created/updated by service (#12150)
<!-- This is an auto-generated comment: release notes by coderabbit.ai -->
## Summary by CodeRabbit

- **New Features**
  - Documents now automatically track and display "created by" and "updated by" user information.
  - Document creation and update timestamps are now managed and shown more accurately.
  - Workspace and document metadata (name, avatar) updates are more responsive and reliable.
  - Document creation supports middleware for customizing properties and behavior.

- **Improvements**
  - Simplified and unified event handling for document list updates, reducing redundant event subscriptions.
  - Enhanced integration of editor and theme settings into the document creation process.
  - Explicit Yjs document initialization for improved workspace stability and reliability.
  - Consolidated journal-related metadata display in document icons and titles for clarity.

- **Bug Fixes**
  - Fixed inconsistencies in how workspace and document names are set and updated.
  - Improved accuracy of "last updated" indicators by handling timestamps automatically.

- **Refactor**
  - Removed deprecated event subjects and direct metadata manipulation in favor of more robust, reactive patterns.
  - Streamlined document creation logic across various features (quick search, journal, recording, etc.).
  - Simplified user avatar display components and removed cloud metadata dependencies.
  - Removed legacy editor setting and theme service dependencies from multiple modules.

- **Chores**
  - Updated internal APIs and interfaces to support new metadata and event handling mechanisms.
  - Cleaned up unused code and dependencies related to editor settings and theme services.
  - Skipped flaky end-to-end test to improve test suite stability.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-05-08 07:53:33 +00:00

180 lines
3.9 KiB
TypeScript

import { Subject } from 'rxjs';
import type * as Y from 'yjs';
import type {
DocMeta,
DocsPropertiesMeta,
WorkspaceMeta,
} from '../extension/index.js';
import { createYProxy } from '../reactive/proxy.js';
type DocCollectionMetaState = {
pages?: unknown[];
properties?: DocsPropertiesMeta;
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();
}
});
};
private _prevDocs = new Set<string>();
protected readonly _proxy: DocCollectionMetaState;
protected readonly _yMap: Y.Map<
DocCollectionMetaState[keyof DocCollectionMetaState]
>;
readonly doc: Y.Doc;
docMetaAdded = new Subject<string>();
docMetaRemoved = new Subject<string>();
docMetaUpdated = new Subject<void>();
readonly id: string = 'meta';
get docMetas() {
if (!this._proxy.pages) {
return [] as DocMeta[];
}
return this._proxy.pages as DocMeta[];
}
get docs() {
return this._proxy.pages;
}
get properties(): DocsPropertiesMeta {
const meta = this._proxy.properties;
if (!meta) {
return {
tags: {
options: [],
},
};
}
return meta;
}
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 _handleDocMetaEvent() {
const { docMetas, _prevDocs } = this;
const newDocs = new Set<string>();
docMetas.forEach(docMeta => {
if (!_prevDocs.has(docMeta.id)) {
this.docMetaAdded.next(docMeta.id);
}
newDocs.add(docMeta.id);
});
_prevDocs.forEach(prevDocId => {
const isRemoved = newDocs.has(prevDocId) === false;
if (isRemoved) {
this.docMetaRemoved.next(prevDocId);
}
});
this._prevDocs = newDocs;
this.docMetaUpdated.next();
}
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);
}
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);
}
setProperties(meta: DocsPropertiesMeta) {
this._proxy.properties = meta;
this.docMetaUpdated.next();
}
}