From e13d27ad9c31fdccdbbe1e4023630fe14fd26623 Mon Sep 17 00:00:00 2001 From: alt0 Date: Tue, 10 Jan 2023 18:37:24 +0800 Subject: [PATCH] refactor: add workspace unit --- packages/data-center/src/datacenter.ts | 55 ++++--- packages/data-center/src/index.ts | 2 +- packages/data-center/src/message/index.ts | 4 +- packages/data-center/src/message/message.ts | 2 +- .../data-center/src/provider/affine/affine.ts | 34 ++-- .../src/provider/affine/apis/workspace.ts | 1 - packages/data-center/src/provider/base.ts | 27 ++-- .../src/provider/local/local.spec.ts | 15 +- .../data-center/src/provider/local/local.ts | 36 +++-- packages/data-center/src/provider/utils.ts | 3 + packages/data-center/src/types/index.ts | 22 +-- .../src/workspace-meta-collection.spec.ts | 50 ------ .../src/workspace-meta-collection.ts | 127 --------------- .../src/workspace-unit-collection.spec.ts | 60 +++++++ .../src/workspace-unit-collection.ts | 152 ++++++++++++++++++ packages/data-center/src/workspace-unit.ts | 64 ++++++++ 16 files changed, 386 insertions(+), 268 deletions(-) delete mode 100644 packages/data-center/src/workspace-meta-collection.spec.ts delete mode 100644 packages/data-center/src/workspace-meta-collection.ts create mode 100644 packages/data-center/src/workspace-unit-collection.spec.ts create mode 100644 packages/data-center/src/workspace-unit-collection.ts create mode 100644 packages/data-center/src/workspace-unit.ts diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 835001395..d65ddc3be 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -1,10 +1,14 @@ -import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; -import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; +import { WorkspaceUnitCollection } from './workspace-unit-collection.js'; +import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; -import { BaseProvider } from './provider/base'; +import type { + BaseProvider, + CreateWorkspaceInfoParams, + UpdateWorkspaceMetaParams, +} from './provider/base'; import { LocalProvider } from './provider/local/local'; import { AffineProvider } from './provider'; -import type { Message, WorkspaceMeta } from './types'; +import type { Message } from './types'; import assert from 'assert'; import { getLogger } from './logger'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; @@ -16,7 +20,7 @@ import { MessageCenter } from './message'; * @classdesc Data center is made for managing different providers for business */ export class DataCenter { - private readonly _workspaceMetaCollection = new WorkspaceMetaCollection(); + private readonly _workspaceUnitCollection = new WorkspaceUnitCollection(); private readonly _logger = getLogger('dc'); private _workspaceInstances: Map = new Map(); private _messageCenter = new MessageCenter(); @@ -35,7 +39,7 @@ export class DataCenter { const getInitParams = () => { return { logger: dc._logger, - workspaces: dc._workspaceMetaCollection.createScope(), + workspaces: dc._workspaceUnitCollection.createScope(), messageCenter: dc._messageCenter, }; }; @@ -68,7 +72,7 @@ export class DataCenter { } public get workspaces() { - return this._workspaceMetaCollection.workspaces; + return this._workspaceUnitCollection.workspaces; } public async refreshWorkspaces() { @@ -82,17 +86,15 @@ export class DataCenter { * @param {string} name workspace name * @returns {Promise} */ - public async createWorkspace(workspaceMeta: WorkspaceMeta) { + public async createWorkspace(params: CreateWorkspaceInfoParams) { assert( this._mainProvider, 'There is no provider. You should add provider first.' ); - const workspaceInfo = await this._mainProvider.createWorkspaceInfo( - workspaceMeta - ); + const workspaceMeta = await this._mainProvider.createWorkspaceInfo(params); - const workspace = createBlocksuiteWorkspace(workspaceInfo.id); + const workspace = createBlocksuiteWorkspace(workspaceMeta.id); await this._mainProvider.createWorkspace(workspace, workspaceMeta); return workspace; @@ -103,7 +105,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ public async deleteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); assert(provider, `Workspace exists, but we couldn't find its provider.`); @@ -115,7 +117,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ private _getBlocksuiteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); return ( this._workspaceInstances.get(workspaceId) || @@ -149,7 +151,7 @@ export class DataCenter { * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const currentProvider = this.providerMap.get(workspaceInfo.provider); if (currentProvider) { @@ -180,9 +182,9 @@ export class DataCenter { * @param {Function} callback callback function */ public async onWorkspacesChange( - callback: (workspaces: WorkspaceMetaCollectionChangeEvent) => void + callback: (workspaces: WorkspaceUnitCollectionChangeEvent) => void ) { - this._workspaceMetaCollection.on('change', callback); + this._workspaceUnitCollection.on('change', callback); } /** @@ -191,11 +193,11 @@ export class DataCenter { * @param {BlocksuiteWorkspace} workspace workspace instance */ public async updateWorkspaceMeta( - { name, avatar }: Partial, + { name, avatar }: UpdateWorkspaceMetaParams, workspace: BlocksuiteWorkspace ) { assert(workspace?.room, 'No workspace to set meta'); - const update: Partial = {}; + const update: Partial = {}; if (name) { workspace.meta.setName(name); update.name = name; @@ -205,7 +207,7 @@ export class DataCenter { update.avatar = avatar; } // may run for change workspace meta - const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); + const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); provider?.updateWorkspaceMeta(workspace.room, update); @@ -217,7 +219,7 @@ export class DataCenter { * @param id workspace id */ public async leaveWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -227,7 +229,7 @@ export class DataCenter { } public async setWorkspacePublish(workspaceId: string, isPublish: boolean) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -236,7 +238,7 @@ export class DataCenter { } public async inviteMember(id: string, email: string) { - const workspaceInfo = this._workspaceMetaCollection.find(id); + const workspaceInfo = this._workspaceUnitCollection.find(id); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -249,7 +251,7 @@ export class DataCenter { * @param {number} permissionId permission id */ public async removeMember(workspaceId: string, permissionId: number) { - const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -280,7 +282,7 @@ export class DataCenter { providerId: string ) { assert(workspace.room, 'No workspace id'); - const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); + const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); if (workspaceInfo.provider === providerId) { this._logger('Workspace provider is same'); @@ -293,11 +295,12 @@ export class DataCenter { this._logger(`create ${providerId} workspace: `, workspaceInfo.name); const newWorkspaceInfo = await newProvider.createWorkspaceInfo({ name: workspaceInfo.name, - avatar: workspaceInfo.avatar, + // avatar: workspaceInfo.avatar, }); const newWorkspace = createBlocksuiteWorkspace(newWorkspaceInfo.id); // TODO optimize this function await newProvider.createWorkspace(newWorkspace, { + ...newWorkspaceInfo, name: workspaceInfo.name, avatar: workspaceInfo.avatar, }); diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts index f758d9c33..029dffd7e 100644 --- a/packages/data-center/src/index.ts +++ b/packages/data-center/src/index.ts @@ -26,6 +26,6 @@ const _initializeDataCenter = () => { export const getDataCenter = _initializeDataCenter(); export type { AccessTokenMessage } from './provider/affine/apis'; -export type { WorkspaceInfo } from './types'; +export { WorkspaceUnit } from './workspace-unit'; export { getLogger } from './logger'; export * from './message'; diff --git a/packages/data-center/src/message/index.ts b/packages/data-center/src/message/index.ts index b04581df1..7d42b90d8 100644 --- a/packages/data-center/src/message/index.ts +++ b/packages/data-center/src/message/index.ts @@ -1,2 +1,2 @@ -export { MessageCenter } from './message'; -export { MessageCode } from './code'; +export { MessageCenter } from './message.js'; +export { MessageCode } from './code.js'; diff --git a/packages/data-center/src/message/message.ts b/packages/data-center/src/message/message.ts index 34d687021..6de9c7579 100644 --- a/packages/data-center/src/message/message.ts +++ b/packages/data-center/src/message/message.ts @@ -1,6 +1,6 @@ import { Observable } from 'lib0/observable'; import { Message } from '../types'; -import { MessageCode } from './code'; +import { MessageCode } from './code.js'; export class MessageCenter extends Observable { constructor() { diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 478aa583d..52a14a60a 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -1,6 +1,11 @@ import { BaseProvider } from '../base.js'; -import type { ProviderConstructorParams } from '../base'; -import type { User, WorkspaceInfo, WorkspaceMeta } from '../../types'; +import type { + ProviderConstructorParams, + CreateWorkspaceInfoParams, + UpdateWorkspaceMetaParams, + WorkspaceMeta0, +} from '../base'; +import type { User } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BlockSchema } from '@blocksuite/blocks/models'; import { applyUpdate } from 'yjs'; @@ -115,12 +120,13 @@ export class AffineProvider extends BaseProvider { return []; } const workspacesList = await this._apis.getWorkspaces(); - const workspaces: WorkspaceInfo[] = workspacesList.map(w => { + const workspaces: WorkspaceMeta0[] = workspacesList.map(w => { return { ...w, memberCount: 0, name: '', provider: 'affine', + syncMode: 'core', }; }); const workspaceInstances = workspaces.map(({ id }) => { @@ -268,19 +274,17 @@ export class AffineProvider extends BaseProvider { } public override async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { - const { id } = await this._apis.createWorkspace( - meta as Required - ); + meta: CreateWorkspaceInfoParams + ): Promise { + const { id } = await this._apis.createWorkspace(meta); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: id, - isPublish: false, + published: false, avatar: '', owner: await this.getUserInfo(), - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'affine', }; @@ -289,7 +293,7 @@ export class AffineProvider extends BaseProvider { public override async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); @@ -298,13 +302,13 @@ export class AffineProvider extends BaseProvider { this._applyCloudUpdates(blocksuiteWorkspace); this.linkLocal(blocksuiteWorkspace); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: workspaceId, - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'affine', }; diff --git a/packages/data-center/src/provider/affine/apis/workspace.ts b/packages/data-center/src/provider/affine/apis/workspace.ts index 0c145928d..8bdc25b21 100644 --- a/packages/data-center/src/provider/affine/apis/workspace.ts +++ b/packages/data-center/src/provider/affine/apis/workspace.ts @@ -79,7 +79,6 @@ export async function getWorkspaceMembers( export interface CreateWorkspaceParams { name: string; - avatar: string; } export async function createWorkspace( diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index f9851098d..0f6a968a6 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -1,7 +1,8 @@ import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; import { MessageCenter } from '../message'; -import { Logger, User, WorkspaceInfo, WorkspaceMeta } from '../types'; -import type { WorkspaceMetaCollectionScope } from '../workspace-meta-collection'; +import { Logger, User } from '../types'; +import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection'; +import type { WorkspaceUnitCtorParams } from '../workspace-unit'; const defaultLogger = () => { return; @@ -9,13 +10,19 @@ const defaultLogger = () => { export interface ProviderConstructorParams { logger?: Logger; - workspaces: WorkspaceMetaCollectionScope; + workspaces: WorkspaceUnitCollectionScope; messageCenter: MessageCenter; } +export type WorkspaceMeta0 = WorkspaceUnitCtorParams; +export type CreateWorkspaceInfoParams = Pick; +export type UpdateWorkspaceMetaParams = Partial< + Pick +>; + export class BaseProvider { public readonly id: string = 'base'; - protected _workspaces!: WorkspaceMetaCollectionScope; + protected _workspaces!: WorkspaceUnitCollectionScope; protected _logger!: Logger; protected _messageCenter!: MessageCenter; @@ -37,8 +44,8 @@ export class BaseProvider { } public async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { + params: CreateWorkspaceInfoParams + ): Promise { throw new Error(`provider: ${this.id} createWorkspaceInfo Not implemented`); } @@ -70,7 +77,7 @@ export class BaseProvider { /** * load workspaces **/ - public async loadWorkspaces(): Promise { + public async loadWorkspaces(): Promise { throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`); } @@ -157,10 +164,10 @@ export class BaseProvider { */ public async updateWorkspaceMeta( id: string, - meta: Partial + params: UpdateWorkspaceMetaParams ): Promise { id; - meta; + params; return; } @@ -170,7 +177,7 @@ export class BaseProvider { */ public async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { return blocksuiteWorkspace; } diff --git a/packages/data-center/src/provider/local/local.spec.ts b/packages/data-center/src/provider/local/local.spec.ts index eff462ef8..e60ce31aa 100644 --- a/packages/data-center/src/provider/local/local.spec.ts +++ b/packages/data-center/src/provider/local/local.spec.ts @@ -1,13 +1,15 @@ import { test, expect } from '@playwright/test'; -import { WorkspaceMetaCollection } from '../../workspace-meta-collection.js'; +import { WorkspaceUnitCollection } from '../../workspace-unit-collection.js'; import { LocalProvider } from './local.js'; import { createBlocksuiteWorkspace } from '../../utils/index.js'; +import { MessageCenter } from '../../message/index.js'; import 'fake-indexeddb/auto'; test.describe.serial('local provider', () => { - const workspaceMetaCollection = new WorkspaceMetaCollection(); + const workspaceMetaCollection = new WorkspaceUnitCollection(); const provider = new LocalProvider({ workspaces: workspaceMetaCollection.createScope(), + messageCenter: new MessageCenter(), }); const workspaceName = 'workspace-test'; @@ -16,23 +18,20 @@ test.describe.serial('local provider', () => { test('create workspace', async () => { const workspaceInfo = await provider.createWorkspaceInfo({ name: workspaceName, - avatar: 'avatar-url-test', }); workspaceId = workspaceInfo.id; const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceId); - await provider.createWorkspace(blocksuiteWorkspace, { - name: workspaceName, - avatar: 'avatar-url-test', - }); + await provider.createWorkspace(blocksuiteWorkspace, workspaceInfo); expect(workspaceMetaCollection.workspaces.length).toEqual(1); expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName); }); test('workspace list cache', async () => { - const workspacesMetaCollection1 = new WorkspaceMetaCollection(); + const workspacesMetaCollection1 = new WorkspaceUnitCollection(); const provider1 = new LocalProvider({ workspaces: workspacesMetaCollection1.createScope(), + messageCenter: new MessageCenter(), }); await provider1.loadWorkspaces(); expect(workspacesMetaCollection1.workspaces.length).toEqual(1); diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index bf8aa3194..f87d7c95f 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -1,7 +1,11 @@ import { BaseProvider } from '../base.js'; -import type { ProviderConstructorParams } from '../base'; +import type { + ProviderConstructorParams, + WorkspaceMeta0, + UpdateWorkspaceMetaParams, + CreateWorkspaceInfoParams, +} from '../base'; import { varStorage as storage } from 'lib0/storage'; -import { WorkspaceInfo, WorkspaceMeta } from '../../types'; import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; import { IndexedDBProvider } from './indexeddb.js'; import assert from 'assert'; @@ -18,7 +22,7 @@ export class LocalProvider extends BaseProvider { this.loadWorkspaces(); } - private _storeWorkspaces(workspaces: WorkspaceInfo[]) { + private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { storage.setItem(WORKSPACE_KEY, JSON.stringify(workspaces)); } @@ -40,12 +44,12 @@ export class LocalProvider extends BaseProvider { return workspace; } - override loadWorkspaces(): Promise { + override loadWorkspaces(): Promise { const workspaceStr = storage.getItem(WORKSPACE_KEY); - let workspaces: WorkspaceInfo[] = []; + let workspaces: WorkspaceMeta0[] = []; if (workspaceStr) { try { - workspaces = JSON.parse(workspaceStr) as WorkspaceInfo[]; + workspaces = JSON.parse(workspaceStr) as WorkspaceMeta0[]; workspaces.forEach(workspace => { this._workspaces.add(workspace); }); @@ -69,22 +73,22 @@ export class LocalProvider extends BaseProvider { public override async updateWorkspaceMeta( id: string, - meta: Partial + meta: UpdateWorkspaceMetaParams ) { this._workspaces.update(id, meta); this._storeWorkspaces(this._workspaces.list()); } public override async createWorkspaceInfo( - meta: WorkspaceMeta - ): Promise { - const workspaceInfo: WorkspaceInfo = { + meta: CreateWorkspaceInfoParams + ): Promise { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: uuidv4(), - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'local', }; @@ -93,20 +97,20 @@ export class LocalProvider extends BaseProvider { public override async createWorkspace( blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta + meta: WorkspaceMeta0 ): Promise { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); assert(meta.name, 'Workspace name is required'); this._logger('Creating affine workspace'); - const workspaceInfo: WorkspaceInfo = { + const workspaceInfo: WorkspaceMeta0 = { name: meta.name, id: workspaceId, - isPublish: false, + published: false, avatar: '', owner: undefined, - isLocal: true, + syncMode: 'core', memberCount: 1, provider: 'local', }; diff --git a/packages/data-center/src/provider/utils.ts b/packages/data-center/src/provider/utils.ts index d8e5f6d05..c193719cc 100644 --- a/packages/data-center/src/provider/utils.ts +++ b/packages/data-center/src/provider/utils.ts @@ -5,6 +5,9 @@ import { getDefaultHeadImgBlob } from '../utils/index.js'; export const setDefaultAvatar = async ( blocksuiteWorkspace: BlocksuiteWorkspace ) => { + if (typeof document === 'undefined') { + return; + } const blob = await getDefaultHeadImgBlob(blocksuiteWorkspace.meta.name); const blobStorage = await blocksuiteWorkspace.blobs; assert(blobStorage, 'No blob storage'); diff --git a/packages/data-center/src/types/index.ts b/packages/data-center/src/types/index.ts index 29e3a2f25..ea85ded82 100644 --- a/packages/data-center/src/types/index.ts +++ b/packages/data-center/src/types/index.ts @@ -1,15 +1,15 @@ import { getLogger } from '../logger'; -export type WorkspaceInfo = { - name: string; - id: string; - isPublish?: boolean; - avatar?: string; - owner?: User; - isLocal?: boolean; - memberCount: number; - provider: string; -}; +// export type WorkspaceInfo = { +// name: string; +// id: string; +// isPublish?: boolean; +// avatar?: string; +// owner?: User; +// isLocal?: boolean; +// memberCount: number; +// provider: string; +// }; export type User = { name: string; @@ -18,7 +18,7 @@ export type User = { avatar: string; }; -export type WorkspaceMeta = Pick; +// export type WorkspaceMeta = Pick; export type Logger = ReturnType; diff --git a/packages/data-center/src/workspace-meta-collection.spec.ts b/packages/data-center/src/workspace-meta-collection.spec.ts deleted file mode 100644 index afb3eb65f..000000000 --- a/packages/data-center/src/workspace-meta-collection.spec.ts +++ /dev/null @@ -1,50 +0,0 @@ -import { test, expect } from '@playwright/test'; -import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; -import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; - -test.describe.serial('workspace meta collection observable', () => { - const workspaces = new WorkspaceMetaCollection(); - - const scope = workspaces.createScope(); - - test('add workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.added?.id).toEqual('123'); - }); - scope.add({ - id: '123', - name: 'test', - memberCount: 1, - provider: '', - }); - }); - - test('list workspace', () => { - const list = scope.list(); - expect(list.length).toEqual(1); - expect(list[0].id).toEqual('123'); - }); - - test('get workspace', () => { - expect(scope.get('123')?.id).toEqual('123'); - }); - - test('update workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.updated?.name).toEqual('demo'); - }); - scope.update('123', { name: 'demo' }); - }); - - test('get workspace form other scope', () => { - const scope1 = workspaces.createScope(); - expect(scope1.get('123')).toBeFalsy(); - }); - - test('delete workspace', () => { - workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { - expect(event.deleted?.id).toEqual('123'); - }); - scope.remove('123'); - }); -}); diff --git a/packages/data-center/src/workspace-meta-collection.ts b/packages/data-center/src/workspace-meta-collection.ts deleted file mode 100644 index 6b7ce4a35..000000000 --- a/packages/data-center/src/workspace-meta-collection.ts +++ /dev/null @@ -1,127 +0,0 @@ -import { Observable } from 'lib0/observable'; -import type { WorkspaceInfo, WorkspaceMeta } from './types'; - -export interface WorkspaceMetaCollectionScope { - get: (workspaceId: string) => WorkspaceInfo | undefined; - list: () => WorkspaceInfo[]; - add: (workspace: WorkspaceInfo) => void; - remove: (workspaceId: string) => boolean; - clear: () => void; - update: (workspaceId: string, workspaceMeta: Partial) => void; -} - -export interface WorkspaceMetaCollectionChangeEvent { - added?: WorkspaceInfo; - deleted?: WorkspaceInfo; - updated?: WorkspaceInfo; -} - -export class WorkspaceMetaCollection extends Observable<'change'> { - private _workspacesMap = new Map(); - - get workspaces(): WorkspaceInfo[] { - return Array.from(this._workspacesMap.values()); - } - - find(workspaceId: string) { - return this._workspacesMap.get(workspaceId); - } - - createScope(): WorkspaceMetaCollectionScope { - const scopedWorkspaceIds = new Set(); - - const get = (workspaceId: string) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return; - } - return this._workspacesMap.get(workspaceId); - }; - - const add = (workspace: WorkspaceInfo) => { - if (this._workspacesMap.has(workspace.id)) { - throw new Error(`Duplicate workspace id.`); - } - this._workspacesMap.set(workspace.id, workspace); - scopedWorkspaceIds.add(workspace.id); - - this.emit('change', [ - { - added: workspace, - } as WorkspaceMetaCollectionChangeEvent, - ]); - }; - - const remove = (workspaceId: string) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return true; - } - - const workspace = this._workspacesMap.get(workspaceId); - if (workspace) { - const ret = this._workspacesMap.delete(workspaceId); - // If deletion failed, return. - if (!ret) { - return ret; - } - - scopedWorkspaceIds.delete(workspaceId); - - this.emit('change', [ - { - deleted: workspace, - } as WorkspaceMetaCollectionChangeEvent, - ]); - } - return true; - }; - - const clear = () => { - scopedWorkspaceIds.forEach(id => { - remove(id); - }); - }; - - const update = ( - workspaceId: string, - workspaceMeta: Partial - ) => { - if (!scopedWorkspaceIds.has(workspaceId)) { - return true; - } - - const workspace = this._workspacesMap.get(workspaceId); - if (!workspace) { - return true; - } - - this._workspacesMap.set(workspaceId, { ...workspace, ...workspaceMeta }); - - this.emit('change', [ - { - updated: this._workspacesMap.get(workspaceId), - } as WorkspaceMetaCollectionChangeEvent, - ]); - }; - - // TODO: need to optimize - const list = () => { - const workspaces: WorkspaceInfo[] = []; - scopedWorkspaceIds.forEach(id => { - const workspace = this._workspacesMap.get(id); - if (workspace) { - workspaces.push(workspace); - } - }); - return workspaces; - }; - - return { - get, - list, - add, - remove, - clear, - update, - }; - } -} diff --git a/packages/data-center/src/workspace-unit-collection.spec.ts b/packages/data-center/src/workspace-unit-collection.spec.ts new file mode 100644 index 000000000..06eee95bd --- /dev/null +++ b/packages/data-center/src/workspace-unit-collection.spec.ts @@ -0,0 +1,60 @@ +import { test, expect } from '@playwright/test'; +import { WorkspaceUnitCollection } from './workspace-unit-collection.js'; +import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection'; + +test.describe.serial('workspace meta collection observable', () => { + const workspaceUnitCollection = new WorkspaceUnitCollection(); + + const scope = workspaceUnitCollection.createScope(); + + test('add workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.added?.id).toEqual('123'); + } + ); + scope.add({ + id: '123', + name: 'test', + memberCount: 1, + provider: '', + syncMode: 'core', + }); + }); + + test('list workspace', () => { + const list = scope.list(); + expect(list.length).toEqual(1); + expect(list[0].id).toEqual('123'); + }); + + test('get workspace', () => { + expect(scope.get('123')?.id).toEqual('123'); + }); + + test('update workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.updated?.name).toEqual('demo'); + } + ); + scope.update('123', { name: 'demo' }); + }); + + test('get workspace form other scope', () => { + const scope1 = workspaceUnitCollection.createScope(); + expect(scope1.get('123')).toBeFalsy(); + }); + + test('delete workspace', () => { + workspaceUnitCollection.once( + 'change', + (event: WorkspaceUnitCollectionChangeEvent) => { + expect(event.deleted?.id).toEqual('123'); + } + ); + scope.remove('123'); + }); +}); diff --git a/packages/data-center/src/workspace-unit-collection.ts b/packages/data-center/src/workspace-unit-collection.ts new file mode 100644 index 000000000..e4dedad8c --- /dev/null +++ b/packages/data-center/src/workspace-unit-collection.ts @@ -0,0 +1,152 @@ +import { Observable } from 'lib0/observable'; +import { WorkspaceUnit } from './workspace-unit.js'; +import type { + WorkspaceUnitCtorParams, + UpdateWorkspaceUnitParams, +} from './workspace-unit'; + +export interface WorkspaceUnitCollectionScope { + get: (workspaceId: string) => WorkspaceUnit | undefined; + list: () => WorkspaceUnit[]; + add: (workspace: WorkspaceUnitCtorParams) => void; + remove: (workspaceId: string) => boolean; + clear: () => void; + update: ( + workspaceId: string, + workspaceMeta: UpdateWorkspaceUnitParams + ) => void; +} + +export interface WorkspaceUnitCollectionChangeEvent { + added?: WorkspaceUnit; + deleted?: WorkspaceUnit; + updated?: WorkspaceUnit; +} + +export class WorkspaceUnitCollection { + private _events = new Observable(); + private _workspaceUnitMap = new Map(); + + get workspaces(): WorkspaceUnit[] { + return Array.from(this._workspaceUnitMap.values()); + } + + public on( + type: 'change', + callback: (event: WorkspaceUnitCollectionChangeEvent) => void + ) { + this._events.on(type, callback); + } + + public once( + type: 'change', + callback: (event: WorkspaceUnitCollectionChangeEvent) => void + ) { + this._events.once(type, callback); + } + + find(workspaceId: string) { + return this._workspaceUnitMap.get(workspaceId); + } + + createScope(): WorkspaceUnitCollectionScope { + const scopedWorkspaceIds = new Set(); + + const get = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return; + } + return this._workspaceUnitMap.get(workspaceId); + }; + + const add = (workspace: WorkspaceUnitCtorParams) => { + if (this._workspaceUnitMap.has(workspace.id)) { + throw new Error(`Duplicate workspace id.`); + } + + const workspaceUnit = new WorkspaceUnit(workspace); + this._workspaceUnitMap.set(workspace.id, workspaceUnit); + + scopedWorkspaceIds.add(workspace.id); + + this._events.emit('change', [ + { + added: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + }; + + const remove = (workspaceId: string) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspaceUnit = this._workspaceUnitMap.get(workspaceId); + if (workspaceUnit) { + const ret = this._workspaceUnitMap.delete(workspaceId); + // If deletion failed, return. + if (!ret) { + return ret; + } + + scopedWorkspaceIds.delete(workspaceId); + + this._events.emit('change', [ + { + deleted: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + } + return true; + }; + + const clear = () => { + scopedWorkspaceIds.forEach(id => { + remove(id); + }); + }; + + const update = ( + workspaceId: string, + workspaceMeta: UpdateWorkspaceUnitParams + ) => { + if (!scopedWorkspaceIds.has(workspaceId)) { + return true; + } + + const workspaceUnit = this._workspaceUnitMap.get(workspaceId); + if (!workspaceUnit) { + return true; + } + + workspaceUnit.update(workspaceMeta); + + this._events.emit('change', [ + { + updated: workspaceUnit, + } as WorkspaceUnitCollectionChangeEvent, + ]); + }; + + // TODO: need to optimize + const list = () => { + const workspaceUnits: WorkspaceUnit[] = []; + scopedWorkspaceIds.forEach(id => { + const workspaceUnit = this._workspaceUnitMap.get(id); + if (workspaceUnit) { + workspaceUnits.push(workspaceUnit); + } + }); + return workspaceUnits; + }; + + return { + get, + list, + add, + remove, + clear, + update, + }; + } +} diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts new file mode 100644 index 000000000..527e8647e --- /dev/null +++ b/packages/data-center/src/workspace-unit.ts @@ -0,0 +1,64 @@ +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; +import type { User } from './types'; + +export type SyncMode = 'all' | 'core'; + +export interface WorkspaceUnitCtorParams { + id: string; + name: string; + avatar?: string; + owner?: User; + published?: boolean; + memberCount: number; + provider: string; + syncMode: SyncMode; + + blocksuiteWorkspace?: BlocksuiteWorkspace; +} + +export type UpdateWorkspaceUnitParams = Partial< + Omit +>; + +export class WorkspaceUnit { + public readonly id: string; + public name!: string; + public avatar?: string; + public owner?: User; + public published?: boolean; + public memberCount!: number; + public provider!: string; + public syncMode: 'all' | 'core' = 'core'; + + private _blocksuiteWorkspace?: BlocksuiteWorkspace; + + constructor(params: WorkspaceUnitCtorParams) { + this.id = params.id; + this.update(params); + } + + get blocksuiteWorkspace() { + return this._blocksuiteWorkspace; + } + + setBlocksuiteWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) { + if (blocksuiteWorkspace?.room !== this.id) { + throw new Error('Workspace id inconsistent.'); + } + this._blocksuiteWorkspace = blocksuiteWorkspace; + } + + update(params: UpdateWorkspaceUnitParams) { + Object.assign(this, params); + } + + get isPublish() { + console.error('Suggest changing to published'); + return this.published; + } + + get isLocal() { + console.error('Suggest changing to syncMode'); + return this.syncMode === 'all'; + } +}