diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 47d4480e71..cd4bbf905a 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -1,23 +1,25 @@ -import { Workspaces } from './workspaces'; -import type { WorkspacesChangeEvent } from './workspaces'; +import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; +import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BaseProvider } from './provider/base'; import { LocalProvider } from './provider/local/local'; import { AffineProvider } from './provider'; -import type { WorkspaceMeta } from './types'; +import type { Message, WorkspaceMeta } from './types'; import assert from 'assert'; import { getLogger } from './logger'; import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { createBlocksuiteWorkspace } from './utils/index.js'; +import { MessageCenter } from './message/message'; /** * @class DataCenter * @classdesc Data center is made for managing different providers for business */ export class DataCenter { - private readonly _workspaces = new Workspaces(); + private readonly _workspaceMetaCollection = new WorkspaceMetaCollection(); private readonly _logger = getLogger('dc'); private _workspaceInstances: Map = new Map(); + private _messageCenter = new MessageCenter(); /** * A mainProvider must exist as the only data trustworthy source. */ @@ -30,19 +32,16 @@ export class DataCenter { static async init(debug: boolean): Promise { const dc = new DataCenter(debug); + const getInitParams = () => { + return { + logger: dc._logger, + workspaces: dc._workspaceMetaCollection.createScope(), + messageCenter: dc._messageCenter, + }; + }; // TODO: switch different provider - dc.registerProvider( - new LocalProvider({ - logger: dc._logger, - workspaces: dc._workspaces.createScope(), - }) - ); - dc.registerProvider( - new AffineProvider({ - logger: dc._logger, - workspaces: dc._workspaces.createScope(), - }) - ); + dc.registerProvider(new LocalProvider(getInitParams())); + dc.registerProvider(new AffineProvider(getInitParams())); return dc; } @@ -69,7 +68,7 @@ export class DataCenter { } public get workspaces() { - return this._workspaces.workspaces; + return this._workspaceMetaCollection.workspaces; } public async refreshWorkspaces() { @@ -104,7 +103,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ public async deleteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.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.`); @@ -116,7 +115,7 @@ export class DataCenter { * @param {string} workspaceId workspace id */ private _getBlocksuiteWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); return ( this._workspaceInstances.get(workspaceId) || @@ -150,7 +149,7 @@ export class DataCenter { * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const currentProvider = this.providerMap.get(workspaceInfo.provider); if (currentProvider) { @@ -181,9 +180,9 @@ export class DataCenter { * @param {Function} callback callback function */ public async onWorkspacesChange( - callback: (workspaces: WorkspacesChangeEvent) => void + callback: (workspaces: WorkspaceMetaCollectionChangeEvent) => void ) { - this._workspaces.on('change', callback); + this._workspaceMetaCollection.on('change', callback); } /** @@ -206,7 +205,7 @@ export class DataCenter { update.avatar = avatar; } // may run for change workspace meta - const workspaceInfo = this._workspaces.find(workspace.room); + const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); provider?.updateWorkspaceMeta(workspace.room, update); @@ -218,7 +217,7 @@ export class DataCenter { * @param id workspace id */ public async leaveWorkspace(workspaceId: string) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -228,7 +227,7 @@ export class DataCenter { } public async setWorkspacePublish(workspaceId: string, isPublish: boolean) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -237,7 +236,7 @@ export class DataCenter { } public async inviteMember(id: string, email: string) { - const workspaceInfo = this._workspaces.find(id); + const workspaceInfo = this._workspaceMetaCollection.find(id); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -250,7 +249,7 @@ export class DataCenter { * @param {number} permissionId permission id */ public async removeMember(workspaceId: string, permissionId: number) { - const workspaceInfo = this._workspaces.find(workspaceId); + const workspaceInfo = this._workspaceMetaCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { @@ -281,7 +280,7 @@ export class DataCenter { providerId: string ) { assert(workspace.room, 'No workspace id'); - const workspaceInfo = this._workspaces.find(workspace.room); + const workspaceInfo = this._workspaceMetaCollection.find(workspace.room); assert(workspaceInfo, 'Workspace not found'); if (workspaceInfo.provider === providerId) { this._logger('Workspace provider is same'); @@ -371,4 +370,8 @@ export class DataCenter { const blobStorage = await workspace.blobs; return (await blobStorage?.set(blob)) || ''; } + + onMessage(cb: (message: Message) => void) { + return this._messageCenter.onMessage(cb); + } } diff --git a/packages/data-center/src/index.ts b/packages/data-center/src/index.ts index 96c281f373..bc816cb3f7 100644 --- a/packages/data-center/src/index.ts +++ b/packages/data-center/src/index.ts @@ -29,3 +29,4 @@ export type { DataCenter }; export type { AccessTokenMessage } from './provider/affine/apis'; export * from './types'; export { getLogger } from './logger'; +export * from './message'; diff --git a/packages/data-center/src/message/code.ts b/packages/data-center/src/message/code.ts new file mode 100644 index 0000000000..271ffc8d38 --- /dev/null +++ b/packages/data-center/src/message/code.ts @@ -0,0 +1,3 @@ +export enum MessageCode { + loginError, +} diff --git a/packages/data-center/src/message/index.ts b/packages/data-center/src/message/index.ts new file mode 100644 index 0000000000..b04581df15 --- /dev/null +++ b/packages/data-center/src/message/index.ts @@ -0,0 +1,2 @@ +export { MessageCenter } from './message'; +export { MessageCode } from './code'; diff --git a/packages/data-center/src/message/message.ts b/packages/data-center/src/message/message.ts new file mode 100644 index 0000000000..7a59f15bf4 --- /dev/null +++ b/packages/data-center/src/message/message.ts @@ -0,0 +1,24 @@ +import { Observable } from 'lib0/observable'; +import { Message } from 'src/types'; +import { MessageCode } from './code'; + +export class MessageCenter extends Observable { + constructor() { + super(); + } + + public send(message: MessageCode) { + this.emit('message', [message]); + } + + public onMessage(callback: (message: Message) => void) { + this.on('message', callback); + } + + private messages: Record = { + [MessageCode.loginError]: { + code: MessageCode.loginError, + message: 'Login failed', + }, + }; +} diff --git a/packages/data-center/src/provider/affine/__tests__/affine.spec.ts b/packages/data-center/src/provider/affine/__tests__/affine.spec.ts index 3208c6d54d..f60af0d139 100644 --- a/packages/data-center/src/provider/affine/__tests__/affine.spec.ts +++ b/packages/data-center/src/provider/affine/__tests__/affine.spec.ts @@ -1,6 +1,6 @@ import { test, expect } from '@playwright/test'; import { AffineProvider } from '../affine.js'; -import { Workspaces } from '../../../workspaces/index.js'; +// import { Workspaces } from '../../../workspaces/index.js'; import { apis } from './mock-apis.js'; import 'fake-indexeddb/auto'; diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index b01766d906..537f54cd74 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -11,6 +11,7 @@ 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 'src/message/code.js'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { @@ -196,7 +197,10 @@ export class AffineProvider extends BaseProvider { return; } } - await this._apis.signInWithGoogle?.(); + const user = await this._apis.signInWithGoogle?.(); + if (!user) { + this._messageCenter.send(MessageCode.loginError); + } } public override async getUserInfo(): Promise { diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index f7a056110c..be075fefe6 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -1,6 +1,7 @@ import { Workspace as BlocksuiteWorkspace, uuidv4 } from '@blocksuite/store'; +import { MessageCenter } from 'src/message'; import { Logger, User, WorkspaceInfo, WorkspaceMeta } from '../types'; -import type { WorkspacesScope } from '../workspaces'; +import type { WorkspaceMetaCollectionScope } from '../workspace-meta-collection'; const defaultLogger = () => { return; @@ -8,17 +9,24 @@ const defaultLogger = () => { export interface ProviderConstructorParams { logger?: Logger; - workspaces: WorkspacesScope; + workspaces: WorkspaceMetaCollectionScope; + messageCenter: MessageCenter; } export class BaseProvider { public readonly id: string = 'base'; - protected _workspaces!: WorkspacesScope; + protected _workspaces!: WorkspaceMetaCollectionScope; protected _logger!: Logger; + protected _messageCenter!: MessageCenter; - public constructor({ logger, workspaces }: ProviderConstructorParams) { + public constructor({ + logger, + workspaces, + messageCenter, + }: ProviderConstructorParams) { this._logger = (logger || defaultLogger) as Logger; this._workspaces = workspaces; + this._messageCenter = messageCenter; } /** diff --git a/packages/data-center/src/provider/local/local.spec.ts b/packages/data-center/src/provider/local/local.spec.ts index 5d388470a6..eff462ef86 100644 --- a/packages/data-center/src/provider/local/local.spec.ts +++ b/packages/data-center/src/provider/local/local.spec.ts @@ -1,13 +1,13 @@ import { test, expect } from '@playwright/test'; -import { Workspaces } from '../../workspaces/index.js'; +import { WorkspaceMetaCollection } from '../../workspace-meta-collection.js'; import { LocalProvider } from './local.js'; import { createBlocksuiteWorkspace } from '../../utils/index.js'; import 'fake-indexeddb/auto'; test.describe.serial('local provider', () => { - const workspaces = new Workspaces(); + const workspaceMetaCollection = new WorkspaceMetaCollection(); const provider = new LocalProvider({ - workspaces: workspaces.createScope(), + workspaces: workspaceMetaCollection.createScope(), }); const workspaceName = 'workspace-test'; @@ -25,30 +25,30 @@ test.describe.serial('local provider', () => { avatar: 'avatar-url-test', }); - expect(workspaces.workspaces.length).toEqual(1); - expect(workspaces.workspaces[0].name).toEqual(workspaceName); + expect(workspaceMetaCollection.workspaces.length).toEqual(1); + expect(workspaceMetaCollection.workspaces[0].name).toEqual(workspaceName); }); test('workspace list cache', async () => { - const workspaces1 = new Workspaces(); + const workspacesMetaCollection1 = new WorkspaceMetaCollection(); const provider1 = new LocalProvider({ - workspaces: workspaces1.createScope(), + workspaces: workspacesMetaCollection1.createScope(), }); await provider1.loadWorkspaces(); - expect(workspaces1.workspaces.length).toEqual(1); - expect(workspaces1.workspaces[0].name).toEqual(workspaceName); - expect(workspaces1.workspaces[0].id).toEqual(workspaceId); + expect(workspacesMetaCollection1.workspaces.length).toEqual(1); + expect(workspacesMetaCollection1.workspaces[0].name).toEqual(workspaceName); + expect(workspacesMetaCollection1.workspaces[0].id).toEqual(workspaceId); }); test('update workspace', async () => { await provider.updateWorkspaceMeta(workspaceId!, { name: '1111', }); - expect(workspaces.workspaces[0].name).toEqual('1111'); + expect(workspaceMetaCollection.workspaces[0].name).toEqual('1111'); }); test('delete workspace', async () => { - expect(workspaces.workspaces.length).toEqual(1); + expect(workspaceMetaCollection.workspaces.length).toEqual(1); /** * FIXME * If we don't wrap setTimeout, @@ -56,8 +56,8 @@ test.describe.serial('local provider', () => { * InvalidStateError: An operation was called on an object on which it is not allowed or at a time when it is not allowed. Also occurs if a request is made on a source object that has been deleted or removed. Use TransactionInactiveError or ReadOnlyError when possible, as they are more specific variations of InvalidStateError. * */ setTimeout(async () => { - await provider.deleteWorkspace(workspaces.workspaces[0].id); - expect(workspaces.workspaces.length).toEqual(0); + await provider.deleteWorkspace(workspaceMetaCollection.workspaces[0].id); + expect(workspaceMetaCollection.workspaces.length).toEqual(0); }, 10); }); }); diff --git a/packages/data-center/src/provider/utils.ts b/packages/data-center/src/provider/utils.ts index b0a08b92d7..d8e5f6d058 100644 --- a/packages/data-center/src/provider/utils.ts +++ b/packages/data-center/src/provider/utils.ts @@ -8,8 +8,7 @@ export const setDefaultAvatar = async ( const blob = await getDefaultHeadImgBlob(blocksuiteWorkspace.meta.name); const blobStorage = await blocksuiteWorkspace.blobs; assert(blobStorage, 'No blob storage'); - const blobId = await blobStorage.set(blob); - const avatar = await blobStorage.get(blobId); + const avatar = await blobStorage.set(blob); if (avatar) { blocksuiteWorkspace.meta.setAvatar(avatar); } diff --git a/packages/data-center/src/types/index.ts b/packages/data-center/src/types/index.ts index 1155ee325a..29e3a2f255 100644 --- a/packages/data-center/src/types/index.ts +++ b/packages/data-center/src/types/index.ts @@ -21,3 +21,8 @@ export type User = { export type WorkspaceMeta = Pick; export type Logger = ReturnType; + +export type Message = { + code: number; + message: string; +}; diff --git a/packages/data-center/src/workspaces/workspaces.spec.ts b/packages/data-center/src/workspace-meta-collection.spec.ts similarity index 64% rename from packages/data-center/src/workspaces/workspaces.spec.ts rename to packages/data-center/src/workspace-meta-collection.spec.ts index c7925fdfff..afb3eb65f5 100644 --- a/packages/data-center/src/workspaces/workspaces.spec.ts +++ b/packages/data-center/src/workspace-meta-collection.spec.ts @@ -1,14 +1,14 @@ import { test, expect } from '@playwright/test'; -import { Workspaces } from './workspaces.js'; -import type { WorkspacesChangeEvent } from './workspaces'; +import { WorkspaceMetaCollection } from './workspace-meta-collection.js'; +import type { WorkspaceMetaCollectionChangeEvent } from './workspace-meta-collection'; -test.describe.serial('workspaces observable', () => { - const workspaces = new Workspaces(); +test.describe.serial('workspace meta collection observable', () => { + const workspaces = new WorkspaceMetaCollection(); const scope = workspaces.createScope(); test('add workspace', () => { - workspaces.once('change', (event: WorkspacesChangeEvent) => { + workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { expect(event.added?.id).toEqual('123'); }); scope.add({ @@ -30,7 +30,7 @@ test.describe.serial('workspaces observable', () => { }); test('update workspace', () => { - workspaces.once('change', (event: WorkspacesChangeEvent) => { + workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { expect(event.updated?.name).toEqual('demo'); }); scope.update('123', { name: 'demo' }); @@ -42,7 +42,7 @@ test.describe.serial('workspaces observable', () => { }); test('delete workspace', () => { - workspaces.once('change', (event: WorkspacesChangeEvent) => { + workspaces.once('change', (event: WorkspaceMetaCollectionChangeEvent) => { expect(event.deleted?.id).toEqual('123'); }); scope.remove('123'); diff --git a/packages/data-center/src/workspaces/workspaces.ts b/packages/data-center/src/workspace-meta-collection.ts similarity index 86% rename from packages/data-center/src/workspaces/workspaces.ts rename to packages/data-center/src/workspace-meta-collection.ts index d248951dc7..6b7ce4a350 100644 --- a/packages/data-center/src/workspaces/workspaces.ts +++ b/packages/data-center/src/workspace-meta-collection.ts @@ -1,7 +1,7 @@ import { Observable } from 'lib0/observable'; -import type { WorkspaceInfo, WorkspaceMeta } from '../types'; +import type { WorkspaceInfo, WorkspaceMeta } from './types'; -export interface WorkspacesScope { +export interface WorkspaceMetaCollectionScope { get: (workspaceId: string) => WorkspaceInfo | undefined; list: () => WorkspaceInfo[]; add: (workspace: WorkspaceInfo) => void; @@ -10,13 +10,13 @@ export interface WorkspacesScope { update: (workspaceId: string, workspaceMeta: Partial) => void; } -export interface WorkspacesChangeEvent { +export interface WorkspaceMetaCollectionChangeEvent { added?: WorkspaceInfo; deleted?: WorkspaceInfo; updated?: WorkspaceInfo; } -export class Workspaces extends Observable<'change'> { +export class WorkspaceMetaCollection extends Observable<'change'> { private _workspacesMap = new Map(); get workspaces(): WorkspaceInfo[] { @@ -27,7 +27,7 @@ export class Workspaces extends Observable<'change'> { return this._workspacesMap.get(workspaceId); } - createScope(): WorkspacesScope { + createScope(): WorkspaceMetaCollectionScope { const scopedWorkspaceIds = new Set(); const get = (workspaceId: string) => { @@ -47,7 +47,7 @@ export class Workspaces extends Observable<'change'> { this.emit('change', [ { added: workspace, - } as WorkspacesChangeEvent, + } as WorkspaceMetaCollectionChangeEvent, ]); }; @@ -69,7 +69,7 @@ export class Workspaces extends Observable<'change'> { this.emit('change', [ { deleted: workspace, - } as WorkspacesChangeEvent, + } as WorkspaceMetaCollectionChangeEvent, ]); } return true; @@ -99,7 +99,7 @@ export class Workspaces extends Observable<'change'> { this.emit('change', [ { updated: this._workspacesMap.get(workspaceId), - } as WorkspacesChangeEvent, + } as WorkspaceMetaCollectionChangeEvent, ]); }; diff --git a/packages/data-center/src/workspaces/index.ts b/packages/data-center/src/workspaces/index.ts deleted file mode 100644 index a8ef316e8d..0000000000 --- a/packages/data-center/src/workspaces/index.ts +++ /dev/null @@ -1,2 +0,0 @@ -export { Workspaces } from './workspaces.js'; -export type { WorkspacesScope, WorkspacesChangeEvent } from './workspaces';