mirror of
https://github.com/toeverything/AFFiNE.git
synced 2026-02-25 18:26:05 +08:00
feat: add channel
This commit is contained in:
@@ -36,7 +36,6 @@
|
|||||||
"ky-universal": "^0.11.0",
|
"ky-universal": "^0.11.0",
|
||||||
"lib0": "^0.2.58",
|
"lib0": "^0.2.58",
|
||||||
"swr": "^2.0.0",
|
"swr": "^2.0.0",
|
||||||
"yjs": "^13.5.44",
|
|
||||||
"y-protocols": "^1.0.5"
|
"y-protocols": "^1.0.5"
|
||||||
},
|
},
|
||||||
"peerDependencies": {
|
"peerDependencies": {
|
||||||
|
|||||||
@@ -7,33 +7,47 @@ import type {
|
|||||||
import type { User } from '../../types';
|
import type { User } from '../../types';
|
||||||
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
import { BlockSchema } from '@blocksuite/blocks/models';
|
import { BlockSchema } from '@blocksuite/blocks/models';
|
||||||
import { applyUpdate, encodeStateAsUpdate } from 'yjs';
|
|
||||||
import { storage } from './storage.js';
|
import { storage } from './storage.js';
|
||||||
import assert from 'assert';
|
import assert from 'assert';
|
||||||
import { WebsocketProvider } from './sync.js';
|
import { WebsocketProvider } from './sync.js';
|
||||||
// import { IndexedDBProvider } from '../local/indexeddb';
|
// 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 type { Apis, WorkspaceDetail, Callback } from './apis';
|
||||||
import { setDefaultAvatar } from '../utils.js';
|
import { setDefaultAvatar } from '../utils.js';
|
||||||
import { MessageCode } from '../../message';
|
import { MessageCode } from '../../message';
|
||||||
import { token } from './apis/token.js';
|
import { token } from './apis/token.js';
|
||||||
|
import { WebsocketClient } from './channel';
|
||||||
|
|
||||||
export interface AffineProviderConstructorParams
|
export interface AffineProviderConstructorParams
|
||||||
extends ProviderConstructorParams {
|
extends ProviderConstructorParams {
|
||||||
apis?: Apis;
|
apis?: Apis;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
Y: { applyUpdate, encodeStateAsUpdate },
|
||||||
|
} = BlocksuiteWorkspace;
|
||||||
|
|
||||||
export class AffineProvider extends BaseProvider {
|
export class AffineProvider extends BaseProvider {
|
||||||
public id = 'affine';
|
public id = 'affine';
|
||||||
private _workspacesCache: Map<string, BlocksuiteWorkspace> = new Map();
|
private _workspacesCache: Map<string, BlocksuiteWorkspace> = new Map();
|
||||||
private _onTokenRefresh?: Callback = undefined;
|
private _onTokenRefresh?: Callback = undefined;
|
||||||
private _wsMap: Map<string, WebsocketProvider> = new Map();
|
private _wsMap: Map<string, WebsocketProvider> = new Map();
|
||||||
private _apis: Apis;
|
private _apis: Apis;
|
||||||
|
private _channel: WebsocketClient;
|
||||||
// private _idbMap: Map<string, IndexedDBProvider> = new Map();
|
// private _idbMap: Map<string, IndexedDBProvider> = new Map();
|
||||||
|
|
||||||
constructor({ apis, ...params }: AffineProviderConstructorParams) {
|
constructor({ apis, ...params }: AffineProviderConstructorParams) {
|
||||||
super(params);
|
super(params);
|
||||||
this._apis = apis || getApis();
|
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() {
|
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) {
|
private _getWebsocketProvider(workspace: BlocksuiteWorkspace) {
|
||||||
const { doc, room } = workspace;
|
const { doc, room } = workspace;
|
||||||
assert(room);
|
assert(room);
|
||||||
@@ -206,6 +229,9 @@ export class AffineProvider extends BaseProvider {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
const user = await this._apis.signInWithGoogle?.();
|
const user = await this._apis.signInWithGoogle?.();
|
||||||
|
if (!this._channel.connected) {
|
||||||
|
this._connectChannel();
|
||||||
|
}
|
||||||
if (!user) {
|
if (!user) {
|
||||||
this._messageCenter.send(MessageCode.loginError);
|
this._messageCenter.send(MessageCode.loginError);
|
||||||
}
|
}
|
||||||
@@ -363,6 +389,8 @@ export class AffineProvider extends BaseProvider {
|
|||||||
|
|
||||||
public override async logout(): Promise<void> {
|
public override async logout(): Promise<void> {
|
||||||
token.clear();
|
token.clear();
|
||||||
|
this._channel.disconnect();
|
||||||
|
this._wsMap.forEach(ws => ws.disconnect());
|
||||||
storage.removeItem('token');
|
storage.removeItem('token');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
53
packages/data-center/src/provider/affine/channel.ts
Normal file
53
packages/data-center/src/provider/affine/channel.ts
Normal file
@@ -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);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,14 +1,19 @@
|
|||||||
/* eslint-disable @typescript-eslint/no-explicit-any */
|
/* eslint-disable @typescript-eslint/no-explicit-any */
|
||||||
import * as idb from 'lib0/indexeddb.js';
|
import * as idb from 'lib0/indexeddb.js';
|
||||||
import { Observable } from 'lib0/observable.js';
|
import { Observable } from 'lib0/observable.js';
|
||||||
import type { Doc } from 'yjs';
|
import { Workspace as BlocksuiteWorkspace } from '@blocksuite/store';
|
||||||
import { applyUpdate, encodeStateAsUpdate, transact } from 'yjs';
|
|
||||||
|
|
||||||
const customStoreName = 'custom';
|
const customStoreName = 'custom';
|
||||||
const updatesStoreName = 'updates';
|
const updatesStoreName = 'updates';
|
||||||
|
|
||||||
const PREFERRED_TRIM_SIZE = 500;
|
const PREFERRED_TRIM_SIZE = 500;
|
||||||
|
|
||||||
|
const {
|
||||||
|
Y: { applyUpdate, transact, encodeStateAsUpdate },
|
||||||
|
} = BlocksuiteWorkspace;
|
||||||
|
|
||||||
|
type Doc = Parameters<typeof transact>[0];
|
||||||
|
|
||||||
const fetchUpdates = async (provider: IndexedDBProvider) => {
|
const fetchUpdates = async (provider: IndexedDBProvider) => {
|
||||||
const [updatesStore] = idb.transact(provider.db as IDBDatabase, [
|
const [updatesStore] = idb.transact(provider.db as IDBDatabase, [
|
||||||
updatesStoreName,
|
updatesStoreName,
|
||||||
|
|||||||
52
pnpm-lock.yaml
generated
52
pnpm-lock.yaml
generated
@@ -140,10 +140,9 @@ importers:
|
|||||||
swr: ^2.0.0
|
swr: ^2.0.0
|
||||||
typescript: ^4.8.4
|
typescript: ^4.8.4
|
||||||
y-protocols: ^1.0.5
|
y-protocols: ^1.0.5
|
||||||
yjs: ^13.5.44
|
|
||||||
dependencies:
|
dependencies:
|
||||||
'@blocksuite/blocks': 0.4.0-20230110112105-ef07332_yjs@13.5.44
|
'@blocksuite/blocks': 0.4.0-20230110112105-ef07332
|
||||||
'@blocksuite/store': 0.4.0-20230110112105-ef07332_yjs@13.5.44
|
'@blocksuite/store': 0.4.0-20230110112105-ef07332
|
||||||
debug: 4.3.4
|
debug: 4.3.4
|
||||||
encoding: 0.1.13
|
encoding: 0.1.13
|
||||||
firebase: 9.15.0_encoding@0.1.13
|
firebase: 9.15.0_encoding@0.1.13
|
||||||
@@ -153,7 +152,6 @@ importers:
|
|||||||
lib0: 0.2.58
|
lib0: 0.2.58
|
||||||
swr: 2.0.0
|
swr: 2.0.0
|
||||||
y-protocols: 1.0.5
|
y-protocols: 1.0.5
|
||||||
yjs: 13.5.44
|
|
||||||
devDependencies:
|
devDependencies:
|
||||||
'@playwright/test': 1.29.1
|
'@playwright/test': 1.29.1
|
||||||
'@types/debug': 4.1.7
|
'@types/debug': 4.1.7
|
||||||
@@ -1455,6 +1453,26 @@ packages:
|
|||||||
to-fast-properties: 2.0.0
|
to-fast-properties: 2.0.0
|
||||||
dev: true
|
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:
|
/@blocksuite/blocks/0.4.0-20230110112105-ef07332_yjs@13.5.44:
|
||||||
resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==}
|
resolution: {integrity: sha512-dtwZRCWtirmheRQaITPOC/D9LZ3yFuYztqL/y2mz/BRSfHj8I71OVcX0HjFXx2TUdzhkea1GNYbp4226zZIiRA==}
|
||||||
dependencies:
|
dependencies:
|
||||||
@@ -1500,6 +1518,12 @@ packages:
|
|||||||
react: 18.2.0
|
react: 18.2.0
|
||||||
dev: false
|
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:
|
/@blocksuite/phasor/0.4.0-20230110112105-ef07332_yjs@13.5.44:
|
||||||
resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==}
|
resolution: {integrity: sha512-R5j/iK7WBFSk7vSk8HEAsxDwr+xDeY7JuzdGzzwnkcOaxuM6Eea6g0vMw7DPuWDAkvlcP7JsgXLnzWgFZh7qmA==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
@@ -1508,6 +1532,26 @@ packages:
|
|||||||
yjs: 13.5.44
|
yjs: 13.5.44
|
||||||
dev: false
|
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:
|
/@blocksuite/store/0.4.0-20230110112105-ef07332_yjs@13.5.44:
|
||||||
resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==}
|
resolution: {integrity: sha512-NisHLf0uSyFu5DUZD13QSsp33C9vnd7Jf7jdLOAPztQ2U05NcGFopjM2InhwBmtmQSHrd/qi25PjgnAJ7/HSNQ==}
|
||||||
peerDependencies:
|
peerDependencies:
|
||||||
|
|||||||
Reference in New Issue
Block a user