* Creates a new TLSyncClient instance to manage synchronization with a remote server. * * @param config - Configuration object for the sync client * - store - The local tldraw store to synchronize * - socket - WebSocket adapter for server communication * - presence - Reactive signal
(config: {
store: S
socket: TLPersistentClientSocket<any, any>
presence: Signal<R | null>
presenceMode?: Signal<TLPresenceMode>
onLoad(self: TLSyncClient<R, S>): void
onSyncError(reason: string): void
onCustomMessageReceived?: TLCustomMessageHandler
onAfterConnect?(self: TLSyncClient<R, S>, details: { isReadonly: boolean }): void
didCancel?(): boolean
})
| 471 | * - didCancel - Optional function to check if sync should be cancelled |
| 472 | */ |
| 473 | constructor(config: { |
| 474 | store: S |
| 475 | socket: TLPersistentClientSocket<any, any> |
| 476 | presence: Signal<R | null> |
| 477 | presenceMode?: Signal<TLPresenceMode> |
| 478 | onLoad(self: TLSyncClient<R, S>): void |
| 479 | onSyncError(reason: string): void |
| 480 | onCustomMessageReceived?: TLCustomMessageHandler |
| 481 | onAfterConnect?(self: TLSyncClient<R, S>, details: { isReadonly: boolean }): void |
| 482 | didCancel?(): boolean |
| 483 | }) { |
| 484 | this.didCancel = config.didCancel |
| 485 | |
| 486 | this.presenceType = config.store.scopedTypes.presence.values().next().value ?? null |
| 487 | |
| 488 | // Create a separate throttle instance for network sync operations |
| 489 | // This ensures sync operations have their own queue separate from UI operations |
| 490 | this.fpsScheduler = new FpsScheduler(COLLABORATIVE_MODE_FPS) |
| 491 | |
| 492 | // Initialize throttled methods after throttle instance is created |
| 493 | this.sendUnsentChanges = this.fpsScheduler.fpsThrottle(() => { |
| 494 | this.debug('sending unsent changes', { |
| 495 | isConnectedToRoom: this.isConnectedToRoom, |
| 496 | unsentChanges: this.unsentChanges, |
| 497 | }) |
| 498 | if (!this.isConnectedToRoom || this.store.isPossiblyCorrupted()) { |
| 499 | return |
| 500 | } |
| 501 | if (!this.unsentChanges.nextDiff && !this.unsentChanges.nextPresence) { |
| 502 | return |
| 503 | } |
| 504 | const diff = this.unsentChanges.nextDiff |
| 505 | ? (getNetworkDiff(this.unsentChanges.nextDiff) ?? undefined) |
| 506 | : undefined |
| 507 | const presence = this.unsentChanges.nextPresence |
| 508 | ? getPresenceOp<R>(this.lastPushedPresenceState, this.unsentChanges.nextPresence) |
| 509 | : undefined |
| 510 | |
| 511 | if (!diff && !presence) { |
| 512 | return |
| 513 | } |
| 514 | |
| 515 | const pushRequest: TLPushRequest<R> = { |
| 516 | type: 'push', |
| 517 | clientClock: this.clientClock, |
| 518 | diff, |
| 519 | presence, |
| 520 | } |
| 521 | |
| 522 | this.debug('sending push request', pushRequest) |
| 523 | this.socket.sendMessage(pushRequest) |
| 524 | |
| 525 | if (this.unsentChanges.nextPresence) { |
| 526 | this.lastPushedPresenceState = this.unsentChanges.nextPresence |
| 527 | } |
| 528 | this.clientClock++ |
| 529 | this.pendingPushRequests.push(pushRequest) |
| 530 | this.unsentChanges.nextDiff = undefined |
nothing calls this directly
no test coverage detected