refactor(core): move infra modules to core (#9207)

This commit is contained in:
EYHN
2024-12-23 04:53:59 +00:00
parent 2ea79d25ad
commit 129f94ee78
337 changed files with 908 additions and 1367 deletions

View File

@@ -0,0 +1,54 @@
import type { DocCustomPropertyInfo } from '../db';
/**
* default built-in custom property, user can update and delete them
*
* 'id' and 'type' is request, 'index' is a manually maintained incremental key.
*/
export const BUILT_IN_CUSTOM_PROPERTY_TYPE = [
{
id: 'tags',
type: 'tags',
index: 'a0000001',
},
{
id: 'docPrimaryMode',
type: 'docPrimaryMode',
show: 'always-hide',
index: 'a0000002',
},
{
id: 'journal',
type: 'journal',
show: 'always-hide',
index: 'a0000003',
},
{
id: 'createdAt',
type: 'createdAt',
index: 'a0000004',
},
{
id: 'updatedAt',
type: 'updatedAt',
index: 'a0000005',
},
{
id: 'createdBy',
type: 'createdBy',
show: 'always-hide',
index: 'a0000006',
},
{
id: 'edgelessTheme',
type: 'edgelessTheme',
show: 'always-hide',
index: 'a0000007',
},
{
id: 'pageWidth',
type: 'pageWidth',
show: 'always-hide',
index: 'a0000008',
},
] as DocCustomPropertyInfo[];

View File

@@ -0,0 +1,86 @@
import type { DocMode, RootBlockModel } from '@blocksuite/affine/blocks';
import { Entity } from '@toeverything/infra';
import type { WorkspaceService } from '../../workspace';
import type { DocScope } from '../scopes/doc';
import type { DocsStore } from '../stores/docs';
export class Doc extends Entity {
constructor(
public readonly scope: DocScope,
private readonly store: DocsStore,
private readonly workspaceService: WorkspaceService
) {
super();
}
/**
* for convenience
*/
get workspace() {
return this.workspaceService.workspace;
}
get id() {
return this.scope.props.docId;
}
public readonly blockSuiteDoc = this.scope.props.blockSuiteDoc;
public readonly record = this.scope.props.record;
readonly meta$ = this.record.meta$;
readonly properties$ = this.record.properties$;
readonly primaryMode$ = this.record.primaryMode$;
readonly title$ = this.record.title$;
readonly trash$ = this.record.trash$;
customProperty$(propertyId: string) {
return this.record.customProperty$(propertyId);
}
setCustomProperty(propertyId: string, value: string) {
return this.record.setCustomProperty(propertyId, value);
}
setPrimaryMode(mode: DocMode) {
return this.record.setPrimaryMode(mode);
}
getPrimaryMode() {
return this.record.getPrimaryMode();
}
togglePrimaryMode() {
this.setPrimaryMode(
(this.getPrimaryMode() === 'edgeless' ? 'page' : 'edgeless') as DocMode
);
}
moveToTrash() {
return this.record.moveToTrash();
}
restoreFromTrash() {
return this.record.restoreFromTrash();
}
waitForSyncReady() {
return this.store.waitForDocLoadReady(this.id);
}
setPriorityLoad(priority: number) {
return this.store.setPriorityLoad(this.id, priority);
}
changeDocTitle(newTitle: string) {
const pageBlock = this.blockSuiteDoc.getBlocksByFlavour('affine:page').at(0)
?.model as RootBlockModel | undefined;
if (pageBlock) {
this.blockSuiteDoc.transact(() => {
pageBlock.title.delete(0, pageBlock.title.length);
pageBlock.title.insert(newTitle, 0);
});
this.record.setMeta({ title: newTitle });
}
}
}

View File

@@ -0,0 +1,77 @@
import {
Entity,
generateFractionalIndexingKeyBetween,
LiveData,
} from '@toeverything/infra';
import type { DocCustomPropertyInfo } from '../../db/schema/schema';
import type { DocPropertiesStore } from '../stores/doc-properties';
export class DocPropertyList extends Entity {
constructor(private readonly docPropertiesStore: DocPropertiesStore) {
super();
}
properties$ = LiveData.from(
this.docPropertiesStore.watchDocPropertyInfoList(),
[]
);
sortedProperties$ = this.properties$.map(list =>
// default index key is '', so always before any others
list.toSorted((a, b) => ((a.index ?? '') > (b.index ?? '') ? 1 : -1))
);
propertyInfo$(id: string) {
return this.properties$.map(list => list.find(info => info.id === id));
}
updatePropertyInfo(id: string, properties: Partial<DocCustomPropertyInfo>) {
this.docPropertiesStore.updateDocPropertyInfo(id, properties);
}
createProperty(
properties: Omit<DocCustomPropertyInfo, 'id'> & { id?: string }
) {
return this.docPropertiesStore.createDocPropertyInfo(properties);
}
removeProperty(id: string) {
this.docPropertiesStore.removeDocPropertyInfo(id);
}
indexAt(at: 'before' | 'after', targetId?: string) {
const sortedChildren = this.sortedProperties$.value.filter(
node => node.index
) as (DocCustomPropertyInfo & { index: string })[];
const targetIndex = targetId
? sortedChildren.findIndex(node => node.id === targetId)
: -1;
if (targetIndex === -1) {
if (at === 'before') {
const first = sortedChildren.at(0);
return generateFractionalIndexingKeyBetween(null, first?.index ?? null);
} else {
const last = sortedChildren.at(-1);
return generateFractionalIndexingKeyBetween(last?.index ?? null, null);
}
} else {
const target = sortedChildren[targetIndex];
const before: DocCustomPropertyInfo | null =
sortedChildren[targetIndex - 1] || null;
const after: DocCustomPropertyInfo | null =
sortedChildren[targetIndex + 1] || null;
if (at === 'before') {
return generateFractionalIndexingKeyBetween(
before?.index ?? null,
target.index
);
} else {
return generateFractionalIndexingKeyBetween(
target.index,
after?.index ?? null
);
}
}
}
}

View File

@@ -0,0 +1,85 @@
import type { DocMode } from '@blocksuite/affine/blocks';
import { Entity, LiveData } from '@toeverything/infra';
import { map } from 'rxjs';
import type { DocsStore } from '../stores/docs';
import { DocRecord } from './record';
export class DocRecordList extends Entity {
constructor(private readonly store: DocsStore) {
super();
}
private readonly pool = new Map<string, DocRecord>();
public readonly docsMap$ = LiveData.from<Map<string, DocRecord>>(
this.store.watchDocIds().pipe(
map(
ids =>
new Map(
ids.map(id => {
const exists = this.pool.get(id);
if (exists) {
return [id, exists];
}
const record = this.framework.createEntity(DocRecord, { id });
this.pool.set(id, record);
return [id, record];
})
)
)
),
new Map()
);
public readonly docs$ = this.docsMap$.selector(d => Array.from(d.values()));
public readonly trashDocs$ = LiveData.from<DocRecord[]>(
this.store.watchTrashDocIds().pipe(
map(ids =>
ids.map(id => {
const exists = this.pool.get(id);
if (exists) {
return exists;
}
const record = this.framework.createEntity(DocRecord, { id });
this.pool.set(id, record);
return record;
})
)
),
[]
);
public readonly isReady$ = LiveData.from(
this.store.watchDocListReady(),
false
);
public doc$(id: string) {
return this.docsMap$.selector(map => map.get(id));
}
public setPrimaryMode(id: string, mode: DocMode) {
return this.store.setDocPrimaryModeSetting(id, mode);
}
public getPrimaryMode(id: string) {
return this.store.getDocPrimaryModeSetting(id);
}
public togglePrimaryMode(id: string) {
const mode = (
this.getPrimaryMode(id) === 'edgeless' ? 'page' : 'edgeless'
) as DocMode;
this.setPrimaryMode(id, mode);
return this.getPrimaryMode(id);
}
public primaryMode$(id: string) {
return LiveData.from(
this.store.watchDocPrimaryModeSetting(id),
this.getPrimaryMode(id)
);
}
}

View File

@@ -0,0 +1,79 @@
import type { DocMode } from '@blocksuite/affine/blocks';
import type { DocMeta } from '@blocksuite/affine/store';
import { Entity, LiveData } from '@toeverything/infra';
import type { DocProperties } from '../../db';
import type { DocPropertiesStore } from '../stores/doc-properties';
import type { DocsStore } from '../stores/docs';
/**
* # DocRecord
*
* Some data you can use without open a doc.
*/
export class DocRecord extends Entity<{ id: string }> {
id: string = this.props.id;
constructor(
private readonly docsStore: DocsStore,
private readonly docPropertiesStore: DocPropertiesStore
) {
super();
}
meta$ = LiveData.from<Partial<DocMeta>>(
this.docsStore.watchDocMeta(this.id),
{}
);
properties$ = LiveData.from<DocProperties>(
this.docPropertiesStore.watchDocProperties(this.id),
{ id: this.id }
);
customProperty$(propertyId: string) {
return this.properties$.selector(
p => p['custom:' + propertyId]
) as LiveData<string | undefined | null>;
}
setCustomProperty(propertyId: string, value: string) {
this.docPropertiesStore.updateDocProperties(this.id, {
['custom:' + propertyId]: value,
});
}
setProperty(propertyId: string, value: string) {
this.docPropertiesStore.updateDocProperties(this.id, {
[propertyId]: value,
});
}
setMeta(meta: Partial<DocMeta>): void {
this.docsStore.setDocMeta(this.id, meta);
}
primaryMode$: LiveData<DocMode> = LiveData.from(
this.docsStore.watchDocPrimaryModeSetting(this.id),
'page' as DocMode
).map(mode => (mode === 'edgeless' ? 'edgeless' : 'page') as DocMode);
setPrimaryMode(mode: DocMode) {
return this.docsStore.setDocPrimaryModeSetting(this.id, mode);
}
getPrimaryMode() {
return this.docsStore.getDocPrimaryModeSetting(this.id);
}
moveToTrash() {
return this.setMeta({ trash: true, trashDate: Date.now() });
}
restoreFromTrash() {
return this.setMeta({ trash: false, trashDate: undefined });
}
title$ = this.meta$.map(meta => meta.title ?? '');
trash$ = this.meta$.map(meta => meta.trash ?? false);
}

View File

@@ -0,0 +1,5 @@
import { createEvent } from '@toeverything/infra';
import type { DocRecord } from '../entities/record';
export const DocCreated = createEvent<DocRecord>('DocCreated');

View File

@@ -0,0 +1,35 @@
export { Doc } from './entities/doc';
export { DocRecord } from './entities/record';
export { DocRecordList } from './entities/record-list';
export { DocCreated } from './events';
export { DocScope } from './scopes/doc';
export { DocService } from './services/doc';
export { DocsService } from './services/docs';
import type { Framework } from '@toeverything/infra';
import { WorkspaceDBService } from '../db';
import { WorkspaceScope, WorkspaceService } from '../workspace';
import { Doc } from './entities/doc';
import { DocPropertyList } from './entities/property-list';
import { DocRecord } from './entities/record';
import { DocRecordList } from './entities/record-list';
import { DocScope } from './scopes/doc';
import { DocService } from './services/doc';
import { DocsService } from './services/docs';
import { DocPropertiesStore } from './stores/doc-properties';
import { DocsStore } from './stores/docs';
export function configureDocModule(framework: Framework) {
framework
.scope(WorkspaceScope)
.service(DocsService, [DocsStore])
.store(DocPropertiesStore, [WorkspaceService, WorkspaceDBService])
.store(DocsStore, [WorkspaceService, DocPropertiesStore])
.entity(DocRecord, [DocsStore, DocPropertiesStore])
.entity(DocRecordList, [DocsStore])
.entity(DocPropertyList, [DocPropertiesStore])
.scope(DocScope)
.entity(Doc, [DocScope, DocsStore, WorkspaceService])
.service(DocService);
}

View File

@@ -0,0 +1,10 @@
import type { Doc as BlockSuiteDoc } from '@blocksuite/affine/store';
import { Scope } from '@toeverything/infra';
import type { DocRecord } from '../entities/record';
export class DocScope extends Scope<{
docId: string;
record: DocRecord;
blockSuiteDoc: BlockSuiteDoc;
}> {}

View File

@@ -0,0 +1,7 @@
import { Service } from '@toeverything/infra';
import { Doc } from '../entities/doc';
export class DocService extends Service {
public readonly doc = this.framework.createEntity(Doc);
}

View File

@@ -0,0 +1,127 @@
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 type { AffineTextAttributes } from '@blocksuite/affine-shared/types';
import {
type DocProps,
initDocFromProps,
ObjectPool,
Service,
} from '@toeverything/infra';
import type { Doc } from '../entities/doc';
import { DocPropertyList } from '../entities/property-list';
import { DocRecordList } from '../entities/record-list';
import { DocCreated } from '../events';
import { DocScope } from '../scopes/doc';
import type { DocsStore } from '../stores/docs';
import { DocService } from './doc';
const logger = new DebugLogger('DocsService');
export class DocsService extends Service {
list = this.framework.createEntity(DocRecordList);
pool = new ObjectPool<string, Doc>({
onDelete(obj) {
obj.scope.dispose();
},
});
propertyList = this.framework.createEntity(DocPropertyList);
constructor(private readonly store: DocsStore) {
super();
}
open(docId: string) {
const docRecord = this.list.doc$(docId).value;
if (!docRecord) {
throw new Error('Doc record not found');
}
const blockSuiteDoc = this.store.getBlockSuiteDoc(docId);
if (!blockSuiteDoc) {
throw new Error('Doc not found');
}
const exists = this.pool.get(docId);
if (exists) {
return { doc: exists.obj, release: exists.release };
}
const docScope = this.framework.createScope(DocScope, {
docId,
blockSuiteDoc,
record: docRecord,
});
try {
blockSuiteDoc.load();
} catch (e) {
logger.error('Failed to load doc', {
docId,
error: e,
});
}
const doc = docScope.get(DocService).doc;
const { obj, release } = this.pool.put(docId, doc);
return { doc: obj, release };
}
createDoc(
options: {
primaryMode?: DocMode;
docProps?: DocProps;
} = {}
) {
const doc = this.store.createBlockSuiteDoc();
initDocFromProps(doc, options.docProps);
this.store.markDocSyncStateAsReady(doc.id);
const docRecord = this.list.doc$(doc.id).value;
if (!docRecord) {
throw new Unreachable();
}
if (options.primaryMode) {
docRecord.setPrimaryMode(options.primaryMode);
}
this.eventBus.emit(DocCreated, docRecord);
return docRecord;
}
async addLinkedDoc(targetDocId: string, linkedDocId: string) {
const { doc, release } = this.open(targetDocId);
doc.setPriorityLoad(10);
await doc.waitForSyncReady();
const text = new doc.blockSuiteDoc.Text([
{
insert: ' ',
attributes: {
reference: {
type: 'LinkedPage',
pageId: linkedDocId,
},
},
},
] as DeltaInsert<AffineTextAttributes>[]);
const [frame] = doc.blockSuiteDoc.getBlocksByFlavour('affine:note');
frame &&
doc.blockSuiteDoc.addBlock(
'affine:paragraph' as never, // TODO(eyhn): fix type
{ text },
frame.id
);
release();
}
async changeDocTitle(docId: string, newTitle: string) {
const { doc, release } = this.open(docId);
doc.setPriorityLoad(10);
await doc.waitForSyncReady();
doc.changeDocTitle(newTitle);
release();
}
}

View File

@@ -0,0 +1,258 @@
import { Store, yjsObserveByPath, yjsObserveDeep } from '@toeverything/infra';
import { differenceBy, isNil, omitBy } from 'lodash-es';
import { combineLatest, map, switchMap } from 'rxjs';
import { AbstractType as YAbstractType } from 'yjs';
import type { WorkspaceDBService } from '../../db';
import type {
DocCustomPropertyInfo,
DocProperties,
} from '../../db/schema/schema';
import type { WorkspaceService } from '../../workspace';
import { BUILT_IN_CUSTOM_PROPERTY_TYPE } from '../constants';
interface LegacyDocProperties {
custom?: Record<string, { value: unknown } | undefined>;
system?: Record<string, { value: unknown } | undefined>;
}
type LegacyDocPropertyInfo = {
id?: string;
name?: string;
type?: string;
icon?: string;
};
type LegacyDocPropertyInfoList = Record<
string,
LegacyDocPropertyInfo | undefined
>;
export class DocPropertiesStore extends Store {
constructor(
private readonly workspaceService: WorkspaceService,
private readonly dbService: WorkspaceDBService
) {
super();
}
updateDocProperties(id: string, config: Partial<DocProperties>) {
return this.dbService.db.docProperties.create({
id,
...config,
});
}
getDocPropertyInfoList() {
const db = this.dbService.db.docCustomPropertyInfo.find();
const legacy = this.upgradeLegacyDocPropertyInfoList(
this.getLegacyDocPropertyInfoList()
);
const builtIn = BUILT_IN_CUSTOM_PROPERTY_TYPE;
const withLegacy = [...db, ...differenceBy(legacy, db, i => i.id)];
const all = [
...withLegacy,
...differenceBy(builtIn, withLegacy, i => i.id),
];
return all.filter(i => !i.isDeleted);
}
createDocPropertyInfo(
config: Omit<DocCustomPropertyInfo, 'id'> & { id?: string }
) {
return this.dbService.db.docCustomPropertyInfo.create(config);
}
removeDocPropertyInfo(id: string) {
this.updateDocPropertyInfo(id, {
additionalData: {}, // also remove additional data to reduce size
isDeleted: true,
});
}
updateDocPropertyInfo(id: string, config: Partial<DocCustomPropertyInfo>) {
const needMigration = !this.dbService.db.docCustomPropertyInfo.get(id);
const isBuiltIn =
needMigration && BUILT_IN_CUSTOM_PROPERTY_TYPE.some(i => i.id === id);
if (isBuiltIn) {
this.createPropertyFromBuiltIn(id, config);
} else if (needMigration) {
// if this property is not in db, we need to migration it from legacy to db, only type and name is needed
this.migrateLegacyDocPropertyInfo(id, config);
} else {
this.dbService.db.docCustomPropertyInfo.update(id, config);
}
}
migrateLegacyDocPropertyInfo(
id: string,
override: Partial<DocCustomPropertyInfo>
) {
const legacy = this.getLegacyDocPropertyInfo(id);
this.dbService.db.docCustomPropertyInfo.create({
id,
type:
legacy?.type ??
'unknown' /* should never reach here, just for safety, we need handle unknown property type */,
name: legacy?.name,
...override,
});
}
createPropertyFromBuiltIn(
id: string,
override: Partial<DocCustomPropertyInfo>
) {
const builtIn = BUILT_IN_CUSTOM_PROPERTY_TYPE.find(i => i.id === id);
if (!builtIn) {
return;
}
this.createDocPropertyInfo({ ...builtIn, ...override });
}
watchDocPropertyInfoList() {
return combineLatest([
this.watchLegacyDocPropertyInfoList().pipe(
map(this.upgradeLegacyDocPropertyInfoList)
),
this.dbService.db.docCustomPropertyInfo.find$(),
]).pipe(
map(([legacy, db]) => {
const builtIn = BUILT_IN_CUSTOM_PROPERTY_TYPE;
const withLegacy = [...db, ...differenceBy(legacy, db, i => i.id)];
const all = [
...withLegacy,
...differenceBy(builtIn, withLegacy, i => i.id),
];
return all.filter(i => !i.isDeleted);
})
);
}
getDocProperties(id: string) {
return {
...this.upgradeLegacyDocProperties(this.getLegacyDocProperties(id)),
...omitBy(this.dbService.db.docProperties.get(id), isNil),
// db always override legacy, but nil value should not override
};
}
watchDocProperties(id: string) {
return combineLatest([
this.watchLegacyDocProperties(id).pipe(
map(this.upgradeLegacyDocProperties)
),
this.dbService.db.docProperties.get$(id),
]).pipe(
map(
([legacy, db]) =>
({
...legacy,
...omitBy(db, isNil), // db always override legacy, but nil value should not override
}) as DocProperties
)
);
}
private upgradeLegacyDocProperties(properties?: LegacyDocProperties) {
if (!properties) {
return {};
}
const newProperties: Record<string, string> = {};
for (const [key, info] of Object.entries(properties.system ?? {})) {
if (info?.value !== undefined && info.value !== null) {
newProperties[key] = info.value.toString();
}
}
for (const [key, info] of Object.entries(properties.custom ?? {})) {
if (info?.value !== undefined && info.value !== null) {
newProperties['custom:' + key] = info.value.toString();
}
}
return newProperties;
}
private upgradeLegacyDocPropertyInfoList(
infoList?: LegacyDocPropertyInfoList
) {
if (!infoList) {
return [];
}
const newInfoList: DocCustomPropertyInfo[] = [];
for (const [id, info] of Object.entries(infoList ?? {})) {
if (info?.type) {
newInfoList.push({
id,
name: info.name,
type: info.type,
icon: info.icon,
});
}
}
return newInfoList;
}
private getLegacyDocProperties(id: string) {
return this.workspaceService.workspace.rootYDoc
.getMap<any>('affine:workspace-properties')
.get('pageProperties')
?.get(id)
?.toJSON() as LegacyDocProperties | undefined;
}
private watchLegacyDocProperties(id: string) {
return yjsObserveByPath(
this.workspaceService.workspace.rootYDoc.getMap<any>(
'affine:workspace-properties'
),
`pageProperties.${id}`
).pipe(
switchMap(yjsObserveDeep),
map(
p =>
(p instanceof YAbstractType ? p.toJSON() : p) as
| LegacyDocProperties
| undefined
)
);
}
private getLegacyDocPropertyInfoList() {
return this.workspaceService.workspace.rootYDoc
.getMap<any>('affine:workspace-properties')
.get('schema')
?.get('pageProperties')
?.get('custom')
?.toJSON() as LegacyDocPropertyInfoList | undefined;
}
private watchLegacyDocPropertyInfoList() {
return yjsObserveByPath(
this.workspaceService.workspace.rootYDoc.getMap<any>(
'affine:workspace-properties'
),
'schema.pageProperties.custom'
).pipe(
switchMap(yjsObserveDeep),
map(
p =>
(p instanceof YAbstractType ? p.toJSON() : p) as
| LegacyDocPropertyInfoList
| undefined
)
);
}
private getLegacyDocPropertyInfo(id: string) {
return this.workspaceService.workspace.rootYDoc
.getMap<any>('affine:workspace-properties')
.get('schema')
?.get('pageProperties')
?.get('custom')
?.get(id)
?.toJSON() as LegacyDocPropertyInfo | undefined;
}
}

View File

@@ -0,0 +1,144 @@
import type { DocMode } from '@blocksuite/affine/blocks';
import type { DocMeta } from '@blocksuite/affine/store';
import {
Store,
yjsObserve,
yjsObserveByPath,
yjsObserveDeep,
} from '@toeverything/infra';
import { distinctUntilChanged, map, switchMap } from 'rxjs';
import { Array as YArray, Map as YMap } from 'yjs';
import type { WorkspaceService } from '../../workspace';
import type { DocPropertiesStore } from './doc-properties';
export class DocsStore extends Store {
constructor(
private readonly workspaceService: WorkspaceService,
private readonly docPropertiesStore: DocPropertiesStore
) {
super();
}
getBlockSuiteDoc(id: string) {
return this.workspaceService.workspace.docCollection.getDoc(id);
}
createBlockSuiteDoc() {
return this.workspaceService.workspace.docCollection.createDoc();
}
watchDocIds() {
return yjsObserveByPath(
this.workspaceService.workspace.rootYDoc.getMap('meta'),
'pages'
).pipe(
switchMap(yjsObserve),
map(meta => {
if (meta instanceof YArray) {
return meta.map(v => v.get('id') as string);
} else {
return [];
}
})
);
}
watchTrashDocIds() {
return yjsObserveByPath(
this.workspaceService.workspace.rootYDoc.getMap('meta'),
'pages'
).pipe(
switchMap(yjsObserveDeep),
map(meta => {
if (meta instanceof YArray) {
return meta
.map(v => (v.get('trash') ? v.get('id') : null))
.filter(Boolean) as string[];
} else {
return [];
}
})
);
}
watchDocMeta(id: string) {
let docMetaIndexCache = -1;
return yjsObserveByPath(
this.workspaceService.workspace.rootYDoc.getMap('meta'),
'pages'
).pipe(
switchMap(yjsObserve),
map(meta => {
if (meta instanceof YArray) {
if (docMetaIndexCache >= 0) {
const doc = meta.get(docMetaIndexCache);
if (doc && doc.get('id') === id) {
return doc as YMap<any>;
}
}
// meta is YArray, `for-of` is faster then `for`
let i = 0;
for (const doc of meta) {
if (doc && doc.get('id') === id) {
docMetaIndexCache = i;
return doc as YMap<any>;
}
i++;
}
return null;
} else {
return null;
}
}),
switchMap(yjsObserveDeep),
map(meta => {
if (meta instanceof YMap) {
return meta.toJSON() as Partial<DocMeta>;
} else {
return {};
}
})
);
}
watchDocListReady() {
return this.workspaceService.workspace.engine.rootDocState$
.map(state => !state.syncing)
.asObservable();
}
setDocMeta(id: string, meta: Partial<DocMeta>) {
this.workspaceService.workspace.docCollection.setDocMeta(id, meta);
}
setDocPrimaryModeSetting(id: string, mode: DocMode) {
return this.docPropertiesStore.updateDocProperties(id, {
primaryMode: mode,
});
}
getDocPrimaryModeSetting(id: string) {
return this.docPropertiesStore.getDocProperties(id)?.primaryMode;
}
watchDocPrimaryModeSetting(id: string) {
return this.docPropertiesStore.watchDocProperties(id).pipe(
map(config => config?.primaryMode),
distinctUntilChanged((p, c) => p === c)
);
}
waitForDocLoadReady(id: string) {
return this.workspaceService.workspace.engine.doc.waitForReady(id);
}
setPriorityLoad(id: string, priority: number) {
return this.workspaceService.workspace.engine.doc.setPriority(id, priority);
}
markDocSyncStateAsReady(id: string) {
this.workspaceService.workspace.engine.doc.markAsReady(id);
}
}