From 8e4585495f033f89ba794f3c89e4bbc24b039c28 Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 23:14:13 +0800 Subject: [PATCH] refactor: create and load workspace will return workspaceUnit --- packages/data-center/src/datacenter.ts | 61 ++---- .../data-center/src/provider/affine/affine.ts | 201 +++++++----------- .../data-center/src/provider/affine/utils.ts | 95 +++++++++ packages/data-center/src/provider/base.ts | 37 ++-- .../src/provider/local/indexeddb/utils.ts | 29 ++- .../src/provider/local/local.spec.ts | 6 +- .../data-center/src/provider/local/local.ts | 73 ++----- .../data-center/src/provider/local/utils.ts | 34 +++ .../src/workspace-unit-collection.spec.ts | 5 +- .../src/workspace-unit-collection.ts | 37 ++-- packages/data-center/src/workspace-unit.ts | 29 ++- 11 files changed, 327 insertions(+), 280 deletions(-) create mode 100644 packages/data-center/src/provider/affine/utils.ts create mode 100644 packages/data-center/src/provider/local/utils.ts diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index c15861223e..d9269f9887 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -96,12 +96,7 @@ export class DataCenter { 'There is no provider. You should add provider first.' ); - const workspaceMeta = await this._mainProvider.createWorkspaceInfo(params); - - const workspace = createBlocksuiteWorkspace(workspaceMeta.id); - - await this._mainProvider.createWorkspace(workspace, workspaceMeta); - const workspaceUnit = this._workspaceUnitCollection.find(workspaceMeta.id); + const workspaceUnit = await this._mainProvider.createWorkspace(params); return workspaceUnit; } @@ -322,55 +317,33 @@ export class DataCenter { } } - private async _transWorkspaceProvider( - workspace: BlocksuiteWorkspace, - providerId: string + public async enableProvider( + workspaceUnit: WorkspaceUnit, + providerId = 'affine' ) { - assert(workspace.room, 'No workspace id'); - const workspaceInfo = this._workspaceUnitCollection.find(workspace.room); - assert(workspaceInfo, 'Workspace not found'); - if (workspaceInfo.provider === providerId) { + if (workspaceUnit.provider === providerId) { this._logger('Workspace provider is same'); return; } - const currentProvider = this.providerMap.get(workspaceInfo.provider); - assert(currentProvider, 'Provider not found'); - const newProvider = this.providerMap.get(providerId); - assert(newProvider, `provide '${providerId}' is not registered`); - this._logger(`create ${providerId} workspace: `, workspaceInfo.name); - const newWorkspaceInfo = await newProvider.createWorkspaceInfo({ - name: workspaceInfo.name, - // avatar: workspaceInfo.avatar, - }); - const newWorkspace = createBlocksuiteWorkspace(newWorkspaceInfo.id); - // TODO optimize this function - await newProvider.createWorkspace(newWorkspace, { - ...newWorkspaceInfo, - name: workspaceInfo.name, - avatar: workspaceInfo.avatar, - }); + const provider = this.providerMap.get(providerId); + assert(provider); + const newWorkspaceUnit = await provider.extendWorkspace(workspaceUnit); - assert(newWorkspace, 'Create workspace failed'); - this._logger( - `update workspace data from ${workspaceInfo.provider} to ${providerId}` - ); - await newProvider.assign(newWorkspace, workspace); - assert(newWorkspace, 'Create workspace failed'); - await currentProvider.deleteWorkspace(workspace.room); - return newWorkspace.room; + // Currently we only allow enable one provider, so after enable new provider, + // delete the old workspace from its provider. + const oldProvider = this.providerMap.get(workspaceUnit.provider); + assert(oldProvider); + await oldProvider.deleteWorkspace(workspaceUnit.id); + + return newWorkspaceUnit; } /** * Enable workspace cloud * @param {string} id ID of workspace. */ - public async enableWorkspaceCloud(workspace: WorkspaceUnit) { - assert(workspace?.id, 'No workspace to enable cloud'); - assert(workspace.blocksuiteWorkspace); - return await this._transWorkspaceProvider( - workspace.blocksuiteWorkspace, - 'affine' - ); + public async enableWorkspaceCloud(workspaceUnit: WorkspaceUnit) { + return this.enableProvider(workspaceUnit); } /** diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index a3142b74ac..572c05b72c 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -6,7 +6,6 @@ import type { } from '../base'; import type { User } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; -import { BlockSchema } from '@blocksuite/blocks/models'; import { storage } from './storage.js'; import assert from 'assert'; import { WebsocketProvider } from './sync.js'; @@ -14,9 +13,16 @@ import { WebsocketProvider } from './sync.js'; import { getApis } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; -import { MessageCode } from '../../message'; +import { MessageCode } from '../../message/index.js'; import { token } from './apis/token.js'; import { WebsocketClient } from './channel'; +import { + loadWorkspaceUnit, + createWorkspaceUnit, + syncToCloud, +} from './utils.js'; +import { WorkspaceUnit } from '../../workspace-unit.js'; +import { createBlocksuiteWorkspace } from '../../utils/index.js'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { @@ -153,78 +159,25 @@ export class AffineProvider extends BaseProvider { return []; } const workspacesList = await this._apis.getWorkspaces(); - const workspaces: WorkspaceMeta0[] = workspacesList.map(w => { - return { - ...w, - memberCount: 0, - name: '', - provider: 'affine', - syncMode: 'core', - }; - }); - const workspaceInstances = workspaces.map(({ id }) => { - const workspace = - this._workspacesCache.get(id) || - new BlocksuiteWorkspace({ - room: id, - }).register(BlockSchema); - this._workspacesCache.set(id, workspace); - if (workspace) { - return new Promise(resolve => { - this._apis.downloadWorkspace(id).then(data => { - applyUpdate(workspace.doc, new Uint8Array(data)); - resolve(workspace); - }); - }); - } else { - return Promise.resolve(null); - } - }); - - (await Promise.all(workspaceInstances)).forEach((workspace, i) => { - if (workspace) { - workspaces[i] = { - ...workspaces[i], - name: workspace.meta.name, - avatar: workspace.meta.avatar, - }; - } - }); - const getDetailList = workspacesList.map(w => { - const { id } = w; - return new Promise<{ id: string; detail: WorkspaceDetail | null }>( - resolve => { - this._apis.getWorkspaceDetail({ id }).then(data => { - resolve({ id, detail: data || null }); - }); - } - ); - }); - const ownerList = await Promise.all(getDetailList); - (await Promise.all(ownerList)).forEach(detail => { - if (detail) { - const { id, detail: workspaceDetail } = detail; - if (workspaceDetail) { - const { owner, member_count } = workspaceDetail; - const currentWorkspace = workspaces.find(w => w.id === id); - if (currentWorkspace) { - currentWorkspace.owner = { - id: owner.id, - name: owner.name, - avatar: owner.avatar_url, - email: owner.email, - }; - currentWorkspace.memberCount = member_count; - } - } - } - }); - - workspaces.forEach(workspace => { - this._workspaces.add(workspace); - }); - - return workspaces; + const workspaceUnits = await Promise.all( + workspacesList.map(w => { + return loadWorkspaceUnit( + { + id: w.id, + name: '', + avatar: undefined, + owner: undefined, + published: w.public, + memberCount: 1, + provider: 'affine', + syncMode: 'core', + }, + this._apis + ); + }) + ); + this._workspaces.add(workspaceUnits); + return workspaceUnits; } override async auth() { @@ -310,52 +263,29 @@ export class AffineProvider extends BaseProvider { // return workspace; } - public override async createWorkspaceInfo( + public override async createWorkspace( meta: CreateWorkspaceInfoParams - ): Promise { + ): Promise { const { id } = await this._apis.createWorkspace(meta); - const workspaceInfo: WorkspaceMeta0 = { + const workspaceUnit = await createWorkspaceUnit({ + id, name: meta.name, - id: id, - published: false, - avatar: '', + avatar: undefined, owner: await this.getUserInfo(), - syncMode: 'core', - memberCount: 1, - provider: 'affine', - }; - return workspaceInfo; - } - - public override async createWorkspace( - blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta0 - ): Promise { - const workspaceId = blocksuiteWorkspace.room; - assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); - this._logger('Creating affine workspace'); - - this._applyCloudUpdates(blocksuiteWorkspace); - this.linkLocal(blocksuiteWorkspace); - - const workspaceInfo: WorkspaceMeta0 = { - name: meta.name, - id: workspaceId, published: false, - avatar: '', - owner: undefined, - syncMode: 'core', memberCount: 1, provider: 'affine', - }; + syncMode: 'core', + }); - if (!blocksuiteWorkspace.meta.avatar) { - await setDefaultAvatar(blocksuiteWorkspace); - workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar; - } - this._workspaces.add(workspaceInfo); - return blocksuiteWorkspace; + await syncToCloud( + workspaceUnit.blocksuiteWorkspace!, + this._apis.token.refresh + ); + this._workspaces.add(workspaceUnit); + + return workspaceUnit; } public override async publish(id: string, isPublish: boolean): Promise { @@ -377,22 +307,41 @@ export class AffineProvider extends BaseProvider { : null; } - public override async assign( - to: BlocksuiteWorkspace, - from: BlocksuiteWorkspace - ): Promise { - assert(to.room, 'Blocksuite Workspace without room(workspaceId).'); - const ws = this._getWebsocketProvider(to); - applyUpdate(to.doc, encodeStateAsUpdate(from.doc)); - // TODO: upload blobs and make sure doc is synced - await new Promise((resolve, reject) => { - ws.once('synced', () => { - setTimeout(() => resolve(), 1000); - }); - ws.once('lost-connection', () => reject()); - ws.once('connection-error', () => reject()); + public override async extendWorkspace( + workspaceUnit: WorkspaceUnit + ): Promise { + const { id } = await this._apis.createWorkspace({ + name: workspaceUnit.name, }); - return to; + const newWorkspaceUnit = new WorkspaceUnit({ + id, + name: workspaceUnit.name, + avatar: undefined, + owner: await this.getUserInfo(), + published: false, + memberCount: 1, + provider: 'affine', + syncMode: 'core', + }); + + const blocksuiteWorkspace = createBlocksuiteWorkspace(id); + + await new Promise(resolve => { + assert(workspaceUnit.blocksuiteWorkspace); + const doc = blocksuiteWorkspace.doc; + doc.once('update', resolve); + applyUpdate( + doc, + encodeStateAsUpdate(workspaceUnit.blocksuiteWorkspace.doc) + ); + }); + + await syncToCloud(blocksuiteWorkspace, this._apis.token.refresh); + + newWorkspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); + + this._workspaces.add(newWorkspaceUnit); + return newWorkspaceUnit; } public override async logout(): Promise { diff --git a/packages/data-center/src/provider/affine/utils.ts b/packages/data-center/src/provider/affine/utils.ts new file mode 100644 index 0000000000..7fb62e171e --- /dev/null +++ b/packages/data-center/src/provider/affine/utils.ts @@ -0,0 +1,95 @@ +import assert from 'assert'; +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; +import { WorkspaceUnit } from '../../workspace-unit.js'; +import type { WorkspaceUnitCtorParams } from '../../workspace-unit'; +import { createBlocksuiteWorkspace } from '../../utils/index.js'; +import type { Apis } from './apis'; +import { WebsocketProvider } from './sync.js'; +import { setDefaultAvatar } from '../utils.js'; + +export const loadWorkspaceUnit = async ( + params: WorkspaceUnitCtorParams, + apis: Apis +) => { + const workspaceUnit = new WorkspaceUnit(params); + const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id); + + const updates = await apis.downloadWorkspace( + workspaceUnit.id, + params.published + ); + if (updates && updates.byteLength) { + await new Promise(resolve => { + const doc = blocksuiteWorkspace.doc; + doc.once('update', resolve); + BlocksuiteWorkspace.Y.applyUpdate(doc, new Uint8Array(updates)); + }); + } + + const details = await apis.getWorkspaceDetail({ id: workspaceUnit.id }); + const owner = details?.owner; + + workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); + workspaceUnit.update({ + name: blocksuiteWorkspace.meta.name, + avatar: blocksuiteWorkspace.meta.avatar, + memberCount: details?.member_count || 1, + owner: owner + ? { + id: owner.id, + name: owner.name, + avatar: owner.avatar_url, + email: owner.email, + } + : undefined, + }); + + return workspaceUnit; +}; + +export const syncToCloud = async ( + blocksuiteWorkspace: BlocksuiteWorkspace, + refreshToken: string +) => { + const workspaceId = blocksuiteWorkspace.room; + assert(workspaceId, 'Blocksuite workspace without room(workspaceId).'); + + const wsUrl = `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${ + window.location.host + }/api/sync/`; + + const ws = new WebsocketProvider( + wsUrl, + workspaceId, + blocksuiteWorkspace.doc, + { + params: { token: refreshToken }, + } + ); + + await new Promise((resolve, reject) => { + ws.once('synced', () => { + // FIXME: we don't when send local data to cloud successfully, so hack to wait 1s. + // Server will support this by add a new api. + setTimeout(resolve, 1000); + }); + ws.once('lost-connection', () => reject()); + ws.once('connection-error', () => reject()); + }); +}; + +export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => { + const workspaceUnit = new WorkspaceUnit(params); + + const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id); + + blocksuiteWorkspace.meta.setName(workspaceUnit.name); + if (!workspaceUnit.avatar) { + await setDefaultAvatar(blocksuiteWorkspace); + workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar }); + } + + workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); + + return workspaceUnit; +}; diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index fed8626811..c2e0d61ca4 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -1,8 +1,8 @@ -import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { MessageCenter } from '../message'; import { Logger, User } from '../types'; import type { WorkspaceUnitCollectionScope } from '../workspace-unit-collection'; -import type { WorkspaceUnitCtorParams } from '../workspace-unit'; +import type { WorkspaceUnitCtorParams, WorkspaceUnit } from '../workspace-unit'; import { Member } from './affine/apis'; const defaultLogger = () => { @@ -44,12 +44,6 @@ export class BaseProvider { return; } - public async createWorkspaceInfo( - params: CreateWorkspaceInfoParams - ): Promise { - throw new Error(`provider: ${this.id} createWorkspaceInfo Not implemented`); - } - /** * auth provider */ @@ -87,7 +81,7 @@ export class BaseProvider { /** * load workspaces **/ - public async loadWorkspaces(): Promise { + public async loadWorkspaces(): Promise { throw new Error(`provider: ${this.id} loadWorkSpace Not implemented`); } @@ -183,13 +177,18 @@ export class BaseProvider { /** * create workspace by workspace meta - * @param {WorkspaceMeta} meta + * @param {CreateWorkspaceInfoParams} meta */ public async createWorkspace( - blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta0 - ): Promise { - return blocksuiteWorkspace; + meta: CreateWorkspaceInfoParams + ): Promise { + throw new Error(`provider: ${this.id} createWorkspace not implemented`); + } + + public async extendWorkspace( + workspaceUnit: WorkspaceUnit + ): Promise { + throw new Error(`provider: ${this.id} extendWorkspace not implemented`); } /** @@ -214,16 +213,6 @@ export class BaseProvider { return workspace; } - /** - * merge one workspaces to another - * @param workspace - * @returns - */ - public async assign(to: BlocksuiteWorkspace, from: BlocksuiteWorkspace) { - from; - return to; - } - /** * get workspace members * @param {string} workspaceId diff --git a/packages/data-center/src/provider/local/indexeddb/utils.ts b/packages/data-center/src/provider/local/indexeddb/utils.ts index 8ca5c9b46d..10406673f8 100644 --- a/packages/data-center/src/provider/local/indexeddb/utils.ts +++ b/packages/data-center/src/provider/local/indexeddb/utils.ts @@ -2,9 +2,12 @@ import assert from 'assert'; import * as idb from 'lib0/indexeddb.js'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; -const { encodeStateAsUpdate } = BlocksuiteWorkspace.Y; +const { applyUpdate, encodeStateAsUpdate, mergeUpdates } = + BlocksuiteWorkspace.Y; -export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => { +export const writeUpdatesToLocal = async ( + blocksuiteWorkspace: BlocksuiteWorkspace +) => { const workspaceId = blocksuiteWorkspace.room; assert(workspaceId); await idb.deleteDB(workspaceId); @@ -18,3 +21,25 @@ export const initStore = async (blocksuiteWorkspace: BlocksuiteWorkspace) => { await idb.addAutoKey(updatesStore, currState); } }; + +export const applyLocalUpdates = async ( + blocksuiteWorkspace: BlocksuiteWorkspace +) => { + const workspaceId = blocksuiteWorkspace.room; + assert(workspaceId, 'Blocksuite workspace without room(workspaceId).'); + const db = await idb.openDB(workspaceId, db => + idb.createStores(db, [['updates', { autoIncrement: true }], ['custom']]) + ); + + const [updatesStore] = idb.transact(db, ['updates']); // , 'readonly') + if (updatesStore) { + const updates = await idb.getAll(updatesStore); + const doc = blocksuiteWorkspace.doc; + await new Promise(resolve => { + const mergedUpdates = mergeUpdates(updates); + doc.once('update', resolve); + applyUpdate(doc, mergedUpdates); + }); + } + 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 e60ce31aae..ba9cb8b667 100644 --- a/packages/data-center/src/provider/local/local.spec.ts +++ b/packages/data-center/src/provider/local/local.spec.ts @@ -16,12 +16,10 @@ test.describe.serial('local provider', () => { let workspaceId: string | undefined; test('create workspace', async () => { - const workspaceInfo = await provider.createWorkspaceInfo({ + const workspaceUnit = await provider.createWorkspace({ name: workspaceName, }); - workspaceId = workspaceInfo.id; - const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceId); - await provider.createWorkspace(blocksuiteWorkspace, workspaceInfo); + workspaceId = workspaceUnit?.id; expect(workspaceMetaCollection.workspaces.length).toEqual(1); expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName); diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index ec07ef568e..ad71943067 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -8,9 +8,9 @@ import type { import { varStorage as storage } from 'lib0/storage'; import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; import { IndexedDBProvider } from './indexeddb/indexeddb.js'; -import { initStore } from './indexeddb/utils.js'; import assert from 'assert'; -import { setDefaultAvatar } from '../utils.js'; +import { loadWorkspaceUnit, createWorkspaceUnit } from './utils.js'; +import type { WorkspaceUnit } from '../../workspace-unit'; const WORKSPACE_KEY = 'workspaces'; @@ -22,21 +22,12 @@ export class LocalProvider extends BaseProvider { super(params); } - private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { + private _storeWorkspaces(workspaceUnits: WorkspaceUnit[]) { storage.setItem( WORKSPACE_KEY, JSON.stringify( - workspaces.map(w => { - return { - id: w.id, - name: w.name, - avatar: w.avatar, - owner: w.owner, - published: w.published, - memberCount: w.memberCount, - provider: w.provider, - syncMode: w.syncMode, - }; + workspaceUnits.map(w => { + return w.toJSON(); }) ) ); @@ -60,20 +51,23 @@ export class LocalProvider extends BaseProvider { return workspace; } - override loadWorkspaces(): Promise { + override async loadWorkspaces(): Promise { const workspaceStr = storage.getItem(WORKSPACE_KEY); - let workspaces: WorkspaceMeta0[] = []; if (workspaceStr) { try { - workspaces = JSON.parse(workspaceStr) as WorkspaceMeta0[]; - workspaces.forEach(workspace => { - this._workspaces.add(workspace); - }); + const workspaceMetas = JSON.parse(workspaceStr) as WorkspaceMeta0[]; + const workspaceUnits = await Promise.all( + workspaceMetas.map(meta => { + return loadWorkspaceUnit(meta); + }) + ); + this._workspaces.add(workspaceUnits); + return workspaceUnits; } catch (error) { this._logger(`Failed to parse workspaces from storage`); } } - return Promise.resolve(workspaces); + return []; } public override async deleteWorkspace(id: string): Promise { @@ -95,10 +89,10 @@ export class LocalProvider extends BaseProvider { this._storeWorkspaces(this._workspaces.list()); } - public override async createWorkspaceInfo( + public override async createWorkspace( meta: CreateWorkspaceInfoParams - ): Promise { - const workspaceInfo: WorkspaceMeta0 = { + ): Promise { + const workspaceUnit = await createWorkspaceUnit({ name: meta.name, id: uuidv4(), published: false, @@ -107,35 +101,10 @@ export class LocalProvider extends BaseProvider { syncMode: 'core', memberCount: 1, provider: 'local', - }; - return Promise.resolve(workspaceInfo); - } - - public override async createWorkspace( - blocksuiteWorkspace: BlocksuiteWorkspace, - meta: WorkspaceMeta0 - ): Promise { - const workspaceId = blocksuiteWorkspace.room; - assert(workspaceId, 'Blocksuite Workspace without room(workspaceId).'); - this._logger('Creating affine workspace'); - - const workspaceInfo: WorkspaceMeta0 = { - ...meta, - }; - - blocksuiteWorkspace.meta.setName(meta.name); - - if (!meta.avatar) { - await setDefaultAvatar(blocksuiteWorkspace); - workspaceInfo.avatar = blocksuiteWorkspace.meta.avatar; - } - - await initStore(blocksuiteWorkspace); - - this._workspaces.add(workspaceInfo); + }); + this._workspaces.add(workspaceUnit); this._storeWorkspaces(this._workspaces.list()); - - return blocksuiteWorkspace; + return workspaceUnit; } public override async clear(): Promise { diff --git a/packages/data-center/src/provider/local/utils.ts b/packages/data-center/src/provider/local/utils.ts new file mode 100644 index 0000000000..bd5654c51d --- /dev/null +++ b/packages/data-center/src/provider/local/utils.ts @@ -0,0 +1,34 @@ +import { WorkspaceUnit } from '../../workspace-unit.js'; +import type { WorkspaceUnitCtorParams } from '../../workspace-unit'; +import { createBlocksuiteWorkspace } from '../../utils/index.js'; +import { applyLocalUpdates, writeUpdatesToLocal } from './indexeddb/utils.js'; +import { setDefaultAvatar } from '../utils.js'; + +export const loadWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => { + const workspaceUnit = new WorkspaceUnit(params); + + const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id); + + await applyLocalUpdates(blocksuiteWorkspace); + + workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); + + return workspaceUnit; +}; + +export const createWorkspaceUnit = async (params: WorkspaceUnitCtorParams) => { + const workspaceUnit = new WorkspaceUnit(params); + + const blocksuiteWorkspace = createBlocksuiteWorkspace(workspaceUnit.id); + blocksuiteWorkspace.meta.setName(workspaceUnit.name); + if (!workspaceUnit.avatar) { + await setDefaultAvatar(blocksuiteWorkspace); + workspaceUnit.update({ avatar: blocksuiteWorkspace.meta.avatar }); + } + + await writeUpdatesToLocal(blocksuiteWorkspace); + + workspaceUnit.setBlocksuiteWorkspace(blocksuiteWorkspace); + + return workspaceUnit; +}; diff --git a/packages/data-center/src/workspace-unit-collection.spec.ts b/packages/data-center/src/workspace-unit-collection.spec.ts index f010c31d12..405fc82370 100644 --- a/packages/data-center/src/workspace-unit-collection.spec.ts +++ b/packages/data-center/src/workspace-unit-collection.spec.ts @@ -1,6 +1,7 @@ import { test, expect } from '@playwright/test'; import { WorkspaceUnitCollection } from './workspace-unit-collection.js'; import type { WorkspaceUnitCollectionChangeEvent } from './workspace-unit-collection'; +import { WorkspaceUnit } from './workspace-unit.js'; test.describe.serial('workspace meta collection observable', () => { const workspaceUnitCollection = new WorkspaceUnitCollection(); @@ -14,13 +15,15 @@ test.describe.serial('workspace meta collection observable', () => { expect(event.added?.[0]?.id).toEqual('123'); } ); - scope.add({ + const workspaceUnit = new WorkspaceUnit({ id: '123', name: 'test', + avatar: undefined, memberCount: 1, provider: '', syncMode: 'core', }); + scope.add(workspaceUnit); }); test('list workspace', () => { diff --git a/packages/data-center/src/workspace-unit-collection.ts b/packages/data-center/src/workspace-unit-collection.ts index f385cecdca..7d55762560 100644 --- a/packages/data-center/src/workspace-unit-collection.ts +++ b/packages/data-center/src/workspace-unit-collection.ts @@ -1,19 +1,18 @@ import { Observable } from 'lib0/observable'; -import { WorkspaceUnit } from './workspace-unit.js'; import type { - WorkspaceUnitCtorParams, + WorkspaceUnit, UpdateWorkspaceUnitParams, } from './workspace-unit'; export interface WorkspaceUnitCollectionScope { get: (workspaceId: string) => WorkspaceUnit | undefined; list: () => WorkspaceUnit[]; - add: (workspace: WorkspaceUnitCtorParams) => void; + add: (workspace: WorkspaceUnit | WorkspaceUnit[]) => void; remove: (workspaceId: string) => boolean; clear: () => void; update: ( workspaceId: string, - workspaceMeta: UpdateWorkspaceUnitParams + workspaceUnit: UpdateWorkspaceUnitParams ) => void; } @@ -59,20 +58,23 @@ export class WorkspaceUnitCollection { return this._workspaceUnitMap.get(workspaceId); }; - const add = (workspace: WorkspaceUnitCtorParams) => { - if (this._workspaceUnitMap.has(workspace.id)) { - // FIXME: multiple add same workspace - return; - } + const add = (workspaceUnit: WorkspaceUnit | WorkspaceUnit[]) => { + const workspaceUnits = Array.isArray(workspaceUnit) + ? workspaceUnit + : [workspaceUnit]; - const workspaceUnit = new WorkspaceUnit(workspace); - this._workspaceUnitMap.set(workspace.id, workspaceUnit); - - scopedWorkspaceIds.add(workspace.id); + workspaceUnits.forEach(workspaceUnit => { + if (this._workspaceUnitMap.has(workspaceUnit.id)) { + // FIXME: multiple add same workspace + return; + } + this._workspaceUnitMap.set(workspaceUnit.id, workspaceUnit); + scopedWorkspaceIds.add(workspaceUnit.id); + }); this._events.emit('change', [ { - added: [workspaceUnit], + added: workspaceUnits, } as WorkspaceUnitCollectionChangeEvent, ]); }; @@ -107,10 +109,7 @@ export class WorkspaceUnitCollection { }); }; - const update = ( - workspaceId: string, - workspaceMeta: UpdateWorkspaceUnitParams - ) => { + const update = (workspaceId: string, meta: UpdateWorkspaceUnitParams) => { if (!scopedWorkspaceIds.has(workspaceId)) { return true; } @@ -120,7 +119,7 @@ export class WorkspaceUnitCollection { return true; } - workspaceUnit.update(workspaceMeta); + workspaceUnit.update(meta); this._events.emit('change', [ { diff --git a/packages/data-center/src/workspace-unit.ts b/packages/data-center/src/workspace-unit.ts index b7244c0b5e..5bc4bf8aef 100644 --- a/packages/data-center/src/workspace-unit.ts +++ b/packages/data-center/src/workspace-unit.ts @@ -37,6 +37,16 @@ export class WorkspaceUnit { this.update(params); } + get isPublish() { + console.error('Suggest changing to published'); + return this.published; + } + + get isLocal() { + console.error('Suggest changing to syncMode'); + return this.syncMode === 'all'; + } + get blocksuiteWorkspace() { return this._blocksuiteWorkspace; } @@ -52,13 +62,16 @@ export class WorkspaceUnit { 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'; + toJSON(): Omit { + return { + id: this.id, + name: this.name, + avatar: this.avatar, + owner: this.owner, + published: this.published, + memberCount: this.memberCount, + provider: this.provider, + syncMode: this.syncMode, + }; } }