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: