From 250ac0e2eaddb8b47f76fd32499c06a7e0dfd4f2 Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 15:36:13 +0800 Subject: [PATCH 1/5] fix: setBlob and getBlob use WorkspaceUnit --- packages/data-center/src/datacenter.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index 616345be27..f1eb556ecf 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -153,7 +153,7 @@ export class DataCenter { /** * load workspace instance by id * @param {string} workspaceId workspace id - * @returns {Promise} + * @returns {Promise} */ public async loadWorkspace(workspaceId: string) { const workspaceUnit = this._workspaceUnitCollection.find(workspaceId); @@ -206,7 +206,7 @@ export class DataCenter { /** * change workspaces meta * @param {WorkspaceMeta} workspaceMeta workspace meta - * @param {BlocksuiteWorkspace} workspace workspace instance + * @param {WorkspaceUnit} workspace workspace instance */ public async updateWorkspaceMeta( { name, avatar }: UpdateWorkspaceMetaParams, @@ -380,10 +380,10 @@ export class DataCenter { * @returns {Promise} blob url */ async getBlob( - workspace: BlocksuiteWorkspace, + workspaceUnit: WorkspaceUnit, id: string ): Promise { - const blob = await workspace.blobs; + const blob = await workspaceUnit.blocksuiteWorkspace?.blobs; return (await blob?.get(id)) || ''; } @@ -392,8 +392,8 @@ export class DataCenter { * @param id * @returns {Promise} blob url */ - async setBlob(workspace: BlocksuiteWorkspace, blob: Blob): Promise { - const blobStorage = await workspace.blobs; + async setBlob(workspace: WorkspaceUnit, blob: Blob): Promise { + const blobStorage = await workspace.blocksuiteWorkspace?.blobs; return (await blobStorage?.set(blob)) || ''; } From 6459faeeb908378a20fa8adbfd62ff2a5509ae4b Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 16:50:40 +0800 Subject: [PATCH 2/5] fix: local provider save workspaces info --- .../data-center/src/provider/local/local.ts | 18 +++++++++++++++++- 1 file changed, 17 insertions(+), 1 deletion(-) diff --git a/packages/data-center/src/provider/local/local.ts b/packages/data-center/src/provider/local/local.ts index 59ce59a337..ec07ef568e 100644 --- a/packages/data-center/src/provider/local/local.ts +++ b/packages/data-center/src/provider/local/local.ts @@ -23,7 +23,23 @@ export class LocalProvider extends BaseProvider { } private _storeWorkspaces(workspaces: WorkspaceMeta0[]) { - storage.setItem(WORKSPACE_KEY, JSON.stringify(workspaces)); + 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, + }; + }) + ) + ); } public override async linkLocal(workspace: BlocksuiteWorkspace) { From 62826f7ab772239791308dea4c79d2ea253b9be3 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Wed, 11 Jan 2023 17:21:41 +0800 Subject: [PATCH 3/5] feat: add channel --- packages/data-center/package.json | 1 - .../data-center/src/provider/affine/affine.ts | 32 ++++++++++- .../src/provider/affine/channel.ts | 53 +++++++++++++++++++ .../src/provider/local/indexeddb/indexeddb.ts | 9 +++- pnpm-lock.yaml | 52 ++++++++++++++++-- 5 files changed, 138 insertions(+), 9 deletions(-) create mode 100644 packages/data-center/src/provider/affine/channel.ts diff --git a/packages/data-center/package.json b/packages/data-center/package.json index 606f6e196c..15fdc9ba61 100644 --- a/packages/data-center/package.json +++ b/packages/data-center/package.json @@ -36,7 +36,6 @@ "ky-universal": "^0.11.0", "lib0": "^0.2.58", "swr": "^2.0.0", - "yjs": "^13.5.44", "y-protocols": "^1.0.5" }, "peerDependencies": { diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 0794ca10e9..977edb557e 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -7,33 +7,47 @@ import type { import type { User } from '../../types'; import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; import { BlockSchema } from '@blocksuite/blocks/models'; -import { applyUpdate, encodeStateAsUpdate } from 'yjs'; import { storage } from './storage.js'; import assert from 'assert'; import { WebsocketProvider } from './sync.js'; // import { IndexedDBProvider } from '../local/indexeddb'; -import { getApis, Member } from './apis/index.js'; +import { getApis } from './apis/index.js'; import type { Apis, WorkspaceDetail, Callback } from './apis'; import { setDefaultAvatar } from '../utils.js'; import { MessageCode } from '../../message'; import { token } from './apis/token.js'; +import { WebsocketClient } from './channel'; export interface AffineProviderConstructorParams extends ProviderConstructorParams { apis?: Apis; } +const { + Y: { applyUpdate, encodeStateAsUpdate }, +} = BlocksuiteWorkspace; + export class AffineProvider extends BaseProvider { public id = 'affine'; private _workspacesCache: Map = new Map(); private _onTokenRefresh?: Callback = undefined; private _wsMap: Map = new Map(); private _apis: Apis; + private _channel: WebsocketClient; // private _idbMap: Map = new Map(); constructor({ apis, ...params }: AffineProviderConstructorParams) { super(params); this._apis = apis || getApis(); + this._channel = new WebsocketClient( + `${window.location.protocol === 'https:' ? 'wss' : 'ws'}://${ + window.location.host + }/global/sync/`, + this._logger + ); + if (token.isLogin) { + this._connectChannel(); + } } override async init() { @@ -64,6 +78,15 @@ export class AffineProvider extends BaseProvider { } } + private _connectChannel() { + this._channel.connect(); + + // eslint-disable-next-line @typescript-eslint/no-explicit-any + this._channel.on('message', (message: any) => { + console.log('message', message); + }); + } + private _getWebsocketProvider(workspace: BlocksuiteWorkspace) { const { doc, room } = workspace; assert(room); @@ -206,6 +229,9 @@ export class AffineProvider extends BaseProvider { } } const user = await this._apis.signInWithGoogle?.(); + if (!this._channel.connected) { + this._connectChannel(); + } if (!user) { this._messageCenter.send(MessageCode.loginError); } @@ -363,6 +389,8 @@ export class AffineProvider extends BaseProvider { public override async logout(): Promise { token.clear(); + this._channel.disconnect(); + this._wsMap.forEach(ws => ws.disconnect()); storage.removeItem('token'); } diff --git a/packages/data-center/src/provider/affine/channel.ts b/packages/data-center/src/provider/affine/channel.ts new file mode 100644 index 0000000000..8d4ae85b09 --- /dev/null +++ b/packages/data-center/src/provider/affine/channel.ts @@ -0,0 +1,53 @@ +import websocket from 'lib0/websocket'; +import { Logger } from 'src/types'; +import { token } from './apis/token'; + +const RECONNECT_INTERVAL_TIME = 5000; +const MAX_RECONNECT_TIMES = 50; + +export class WebsocketClient extends websocket.WebsocketClient { + public shouldReconnect = false; + private _reconnectInterval: number | null = null; + private _logger: Logger; + constructor( + url: string, + logger: Logger, + options?: { binaryType: 'arraybuffer' | 'blob' | null } + ) { + super(url, options); + this._logger = logger; + this._setupChannel(); + } + + private _setupChannel() { + this.on('connect', () => { + this._logger('Affine channel connected'); + this.shouldReconnect = true; + if (this._reconnectInterval) { + window.clearInterval(this._reconnectInterval); + } + }); + + this.on('disconnect', ({ error }: { error: Error }) => { + if (error) { + let times = 0; + // Try reconnect if connect error has occurred + this._reconnectInterval = window.setInterval(() => { + if (this.shouldReconnect && token.isLogin && !this.connected) { + try { + this.connect(); + this._logger(`try reconnect channel ${++times} times`); + if (times > MAX_RECONNECT_TIMES) { + this._logger('reconnect failed, max reconnect times reached'); + this._reconnectInterval && + window.clearInterval(this._reconnectInterval); + } + } catch (e) { + this._logger('reconnect failed', e); + } + } + }, RECONNECT_INTERVAL_TIME); + } + }); + } +} diff --git a/packages/data-center/src/provider/local/indexeddb/indexeddb.ts b/packages/data-center/src/provider/local/indexeddb/indexeddb.ts index bece021b13..cc79a7d906 100644 --- a/packages/data-center/src/provider/local/indexeddb/indexeddb.ts +++ b/packages/data-center/src/provider/local/indexeddb/indexeddb.ts @@ -1,14 +1,19 @@ /* eslint-disable @typescript-eslint/no-explicit-any */ import * as idb from 'lib0/indexeddb.js'; import { Observable } from 'lib0/observable.js'; -import type { Doc } from 'yjs'; -import { applyUpdate, encodeStateAsUpdate, transact } from 'yjs'; +import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store'; const customStoreName = 'custom'; const updatesStoreName = 'updates'; const PREFERRED_TRIM_SIZE = 500; +const { + Y: { applyUpdate, transact, encodeStateAsUpdate }, +} = BlocksuiteWorkspace; + +type Doc = Parameters[0]; + const fetchUpdates = async (provider: IndexedDBProvider) => { const [updatesStore] = idb.transact(provider.db as IDBDatabase, [ updatesStoreName, diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6f7b81fb5d..e401acf9ae 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -140,10 +140,9 @@ importers: swr: ^2.0.0 typescript: ^4.8.4 y-protocols: ^1.0.5 - yjs: ^13.5.44 dependencies: - '@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44 - '@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44 + '@blocksuite/blocks': 0.4.0-20230110112105-ef07332 + '@blocksuite/store': 0.4.0-20230110112105-ef07332 debug: 4.3.4 encoding: 0.1.13 firebase: 9.15.0_encoding@0.1.13 @@ -153,7 +152,6 @@ importers: lib0: 0.2.58 swr: 2.0.0 y-protocols: 1.0.5 - yjs: 13.5.44 devDependencies: '@playwright/test': 1.29.1 '@types/debug': 4.1.7 @@ -1455,6 +1453,26 @@ packages: to-fast-properties: 2.0.0 dev: true + /@blocksuite/blocks/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==} + dependencies: + '@blocksuite/phasor': 0.4.0-20230110112105-ef07332 + '@blocksuite/store': 0.4.0-20230110112105-ef07332 + '@tldraw/intersect': 1.8.0 + autosize: 5.0.2 + highlight.js: 11.7.0 + hotkeys-js: 3.10.1 + lit: 2.5.0 + perfect-freehand: 1.2.0 + quill: 1.3.7 + quill-cursors: 4.0.0 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + - yjs + dev: false + /@blocksuite/blocks/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==} dependencies: @@ -1500,6 +1518,12 @@ packages: react: 18.2.0 dev: false + /@blocksuite/phasor/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==} + peerDependencies: + yjs: ^13 + dev: false + /@blocksuite/phasor/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==} peerDependencies: @@ -1508,6 +1532,26 @@ packages: yjs: 13.5.44 dev: false + /@blocksuite/store/0.4.0-20230110112105-ef07332: + resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==} + peerDependencies: + yjs: ^13 + dependencies: + '@types/flexsearch': 0.7.3 + '@types/quill': 1.3.10 + buffer: 6.0.3 + flexsearch: 0.7.21 + idb-keyval: 6.2.0 + ky: 0.33.1 + lib0: 0.2.58 + y-protocols: 1.0.5 + y-webrtc: 10.2.3 + transitivePeerDependencies: + - bufferutil + - supports-color + - utf-8-validate + dev: false + /@blocksuite/store/0.4.0-20230110112105-ef07332_yjs@13.5.44: resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==} peerDependencies: From 7e771756793faee5eb480f9c053c7b9f9ce8f3d2 Mon Sep 17 00:00:00 2001 From: MingLiang Wang Date: Wed, 11 Jan 2023 17:30:16 +0800 Subject: [PATCH 4/5] feat: update invite function name --- packages/data-center/src/datacenter.ts | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index f1eb556ecf..d592d2f480 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -255,12 +255,17 @@ export class DataCenter { } } - public async inviteMember(id: string, email: string) { - const workspaceInfo = this._workspaceUnitCollection.find(id); + /** + * invite the new member to the workspace + * @param {string} workspaceId workspace id + * @param {string} email + */ + public async inviteMember(workspaceId: string, email: string) { + const workspaceInfo = this._workspaceUnitCollection.find(workspaceId); assert(workspaceInfo, 'Workspace not found'); const provider = this.providerMap.get(workspaceInfo.provider); if (provider) { - await provider.invite(id, email); + await provider.invite(workspaceId, email); } } From 8b757f538e315ee93fb3ecd3acdf3c1cee5fa1da Mon Sep 17 00:00:00 2001 From: alt0 Date: Wed, 11 Jan 2023 20:20:48 +0800 Subject: [PATCH 5/5] feat: temporary support loading data for public workspace --- packages/data-center/src/datacenter.ts | 25 ++++++++++++++++++- .../data-center/src/provider/affine/affine.ts | 5 ++++ packages/data-center/src/provider/base.ts | 9 +++++++ 3 files changed, 38 insertions(+), 1 deletion(-) diff --git a/packages/data-center/src/datacenter.ts b/packages/data-center/src/datacenter.ts index d592d2f480..d59f090526 100644 --- a/packages/data-center/src/datacenter.ts +++ b/packages/data-center/src/datacenter.ts @@ -13,7 +13,7 @@ import assert from 'assert'; import { getLogger } from './logger'; import { createBlocksuiteWorkspace } from './utils/index.js'; import { MessageCenter } from './message'; -import type { WorkspaceUnit } from './workspace-unit'; +import { WorkspaceUnit } from './workspace-unit'; /** * @class DataCenter @@ -175,6 +175,29 @@ export class DataCenter { return workspaceUnit; } + public async loadPublicWorkspace(workspaceId: string) { + const workspaceUnit = this._workspaceUnitCollection.find(workspaceId); + assert(workspaceUnit, 'Workspace not found'); + const provider = this.providerMap.get(workspaceUnit.provider); + assert(provider); + const blocksuiteWorkspace = this._getBlocksuiteWorkspace(workspaceId); + await provider.loadPublicWorkspace(blocksuiteWorkspace); + + const workspaceUnitForPublic = new WorkspaceUnit({ + id: workspaceUnit.id, + name: workspaceUnit.name, + avatar: workspaceUnit.avatar, + owner: workspaceUnit.owner, + published: workspaceUnit.published, + provider: workspaceUnit.provider, + memberCount: workspaceUnit.memberCount, + syncMode: workspaceUnit.syncMode, + }); + + workspaceUnitForPublic.setBlocksuiteWorkspace(blocksuiteWorkspace); + return workspaceUnitForPublic; + } + /** * get user info by provider id * @param {string} providerId the provider name of workspace diff --git a/packages/data-center/src/provider/affine/affine.ts b/packages/data-center/src/provider/affine/affine.ts index 977edb557e..b0544b1bc7 100644 --- a/packages/data-center/src/provider/affine/affine.ts +++ b/packages/data-center/src/provider/affine/affine.ts @@ -116,6 +116,11 @@ export class AffineProvider extends BaseProvider { } } + override async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) { + await this._applyCloudUpdates(blocksuiteWorkspace); + return blocksuiteWorkspace; + } + override async warpWorkspace(workspace: BlocksuiteWorkspace) { await this._applyCloudUpdates(workspace); const { room } = workspace; diff --git a/packages/data-center/src/provider/base.ts b/packages/data-center/src/provider/base.ts index 17834abf34..fed8626811 100644 --- a/packages/data-center/src/provider/base.ts +++ b/packages/data-center/src/provider/base.ts @@ -75,6 +75,15 @@ export class BaseProvider { return workspace; } + /** + * @deprecated Temporary for public workspace + * @param blocksuiteWorkspace + * @returns + */ + public async loadPublicWorkspace(blocksuiteWorkspace: BlocksuiteWorkspace) { + return blocksuiteWorkspace; + } + /** * load workspaces **/