|
1 | | -import type { EBoard, ModelChangeEvent, ModelService } from "@e-board/board-core"; |
| 1 | +import { OperationSource, type EBoard, type ElementService, type ModelChangeEvent, type ModelService } from "@e-board/board-core"; |
| 2 | +import { WebSocketProvider, MsgType } from '@e-board/board-websocket'; |
| 3 | +const WS_URL = 'ws://localhost:3010/collaboration'; // TODO: 这个配置应该放到app初始化时作为配置传入 |
2 | 4 |
|
3 | 5 | class BoardCollaboration { |
4 | 6 | private modelService: ModelService |
5 | | - // private websocketProvider: WebSocketProvider |
| 7 | + private elementService: ElementService |
| 8 | + private websocketProvider: WebSocketProvider | null = null; |
6 | 9 | private disposeList: (() => void)[] = []; |
| 10 | + private currentUserid = `user-${Math.floor(Math.random() * 1000)}`; |
7 | 11 | constructor(private board: EBoard) { |
| 12 | + |
8 | 13 | this.modelService = board.getService('modelService') |
| 14 | + this.elementService = this.board.getService('elementService') |
| 15 | + |
9 | 16 | this.init() |
10 | 17 | } |
11 | 18 |
|
12 | | - // TODO: 初始化websocketProvider, 监听传入的数据,更新modelService中的数据 |
13 | | - |
14 | 19 | private init = () => { |
| 20 | + this.websocketProvider = new WebSocketProvider(); |
| 21 | + try { |
| 22 | + this.websocketProvider.connect(WS_URL); |
| 23 | + this.websocketProvider.onMessage((data) => { |
| 24 | + console |
| 25 | + if (data.type === MsgType.OPERATION) { |
| 26 | + if (data.senderId === this.currentUserid) return; // 忽略自己发送的消息 |
| 27 | + const operation = JSON.parse(data.data) |
| 28 | + // 外存模型转内存模型 |
| 29 | + if (operation.operation === 'create') { |
| 30 | + const element = this.elementService.getElement(operation.model.type); |
| 31 | + if (!element) throw new Error(`Unregistered element type: ${operation.model.type}`); |
| 32 | + const saveInfoService = this.board.getService('saveInfoService') |
| 33 | + saveInfoService.importSaveInfo(operation.model, OperationSource.REMOTE) |
| 34 | + } else if (operation.operation === 'update') { |
| 35 | + const element = this.elementService.getElement(operation.updates.type); |
| 36 | + if (!element) throw new Error(`Unregistered element type: ${operation.updates.type}`); |
| 37 | + const model = element.saveInfoProvider.importSaveInfo(operation.data.updates) |
| 38 | + this.modelService.updateModel(model.id, model, OperationSource.REMOTE) |
| 39 | + } else if (operation.operation === 'delete') { |
| 40 | + |
| 41 | + operation.data.deletedModels.forEach((m: any) => { |
| 42 | + const element = this.elementService.getElement(m.type); |
| 43 | + if (!element) throw new Error(`Unregistered element type: ${m.type}`); |
| 44 | + const model = element.saveInfoProvider.importSaveInfo(m) |
| 45 | + this.modelService.deleteModel(model.id, OperationSource.REMOTE) |
| 46 | + }) |
| 47 | + } else { |
| 48 | + throw new Error(`Unsupported operation type: ${operation.operation}`); |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + }) |
| 53 | + } catch (err) { |
| 54 | + console.error('WebSocket 连接失败:', err); |
| 55 | + } |
| 56 | + |
| 57 | + |
15 | 58 | const { dispose } = this.modelService.onModelOperation( |
16 | 59 | (operation: ModelChangeEvent) => { |
| 60 | + const type = operation.model?.type |
| 61 | + if (!type) throw new Error('Operation missing type'); |
| 62 | + const element = this.elementService.getElement(type); |
| 63 | + if (!element) throw new Error(`Unregistered element type: ${type}`); |
| 64 | + const body: any = { |
| 65 | + type: MsgType.OPERATION, |
| 66 | + id: `msg-${Date.now()}`, |
| 67 | + senderId: this.currentUserid, |
| 68 | + timestamp: Date.now() |
| 69 | + } |
| 70 | + if (operation.operationSource === 'remote') return; // 忽略远程操作,防止循环广播 |
| 71 | + |
| 72 | + // 内存模型转外存模型 |
| 73 | + if (operation.type === 'create') { |
| 74 | + |
| 75 | + body.data = JSON.stringify({ operation: 'create', model: element.saveInfoProvider.parse(operation.model) }) |
| 76 | + } else if (operation.type === 'update') { |
| 77 | + body.data = JSON.stringify({ |
| 78 | + operation: 'update', |
| 79 | + updates: element.saveInfoProvider.parse(operation.model), |
| 80 | + previousState: element.saveInfoProvider.parse(operation.previousState) |
| 81 | + }) |
| 82 | + } else if (operation.type === 'delete') { |
| 83 | + body.data = JSON.stringify({ |
| 84 | + operation: 'delete', |
| 85 | + deletedModels: Array.from(operation.deletedModels?.values() || []).map(m => element.saveInfoProvider.parse(m)) |
| 86 | + }) |
| 87 | + } else { |
| 88 | + throw new Error(`Unsupported operation type: ${operation.type}`); |
| 89 | + } |
| 90 | + this.websocketProvider?.send(body) |
17 | 91 | console.log('Received operation:', operation); |
18 | 92 | } |
19 | 93 | ) |
|
0 commit comments