* Invoked when the socket connection comes online, either for the first time or as the result of * a reconnect. The goal is to rebase on the server's state and fire off a new push request for * any local changes that were made while offline.
(event: Extract<TLSocketServerSentEvent<R>, { type: 'connect' }>)
| 690 | * any local changes that were made while offline. |
| 691 | */ |
| 692 | private didReconnect(event: Extract<TLSocketServerSentEvent<R>, { type: 'connect' }>) { |
| 693 | this.debug('did reconnect', event) |
| 694 | if (event.connectRequestId !== this.latestConnectRequestId) { |
| 695 | // ignore connect events for old connect requests |
| 696 | return |
| 697 | } |
| 698 | this.latestConnectRequestId = null |
| 699 | |
| 700 | if (this.isConnectedToRoom) { |
| 701 | console.error('didReconnect called while already connected') |
| 702 | this.resetConnection(true) |
| 703 | return |
| 704 | } |
| 705 | if (this.pendingPushRequests.length > 0) { |
| 706 | console.error('pendingPushRequests should already be empty when we reconnect') |
| 707 | this.resetConnection(true) |
| 708 | return |
| 709 | } |
| 710 | // at the end of this process we want to have at most one pending push request |
| 711 | // based on anything inside this.speculativeChanges |
| 712 | transact(() => { |
| 713 | // Now our goal is to rebase on the server's state. |
| 714 | // This means wiping away any peer presence data, which the server will replace in full on every connect. |
| 715 | // If the server does not have enough history to give us a partial document state hydration we will |
| 716 | // also need to wipe away all of our document state before hydrating with the server's state from scratch. |
| 717 | const stashedChanges = this.speculativeChanges |
| 718 | this.speculativeChanges = { added: {} as any, updated: {} as any, removed: {} as any } |
| 719 | |
| 720 | this.store.mergeRemoteChanges(() => { |
| 721 | // gather records to delete in a NetworkDiff |
| 722 | const wipeDiff: NetworkDiff<R> = {} |
| 723 | const wipeAll = event.hydrationType === 'wipe_all' |
| 724 | if (!wipeAll) { |
| 725 | // if we're only wiping presence data, undo the speculative changes first |
| 726 | this.store.applyDiff(reverseRecordsDiff(stashedChanges), { runCallbacks: false }) |
| 727 | } |
| 728 | |
| 729 | // now wipe all presence data and, if needed, all document data |
| 730 | for (const [id, record] of objectMapEntries(this.store.serialize('all'))) { |
| 731 | if ( |
| 732 | (wipeAll && this.store.scopedTypes.document.has(record.typeName)) || |
| 733 | record.typeName === this.presenceType |
| 734 | ) { |
| 735 | wipeDiff[id] = [RecordOpType.Remove] |
| 736 | } |
| 737 | } |
| 738 | |
| 739 | // then apply the upstream changes |
| 740 | this.applyNetworkDiff({ ...wipeDiff, ...event.diff }, true) |
| 741 | |
| 742 | this.isConnectedToRoom = true |
| 743 | |
| 744 | // now re-apply the speculative changes creating a new push request with the |
| 745 | // appropriate diff |
| 746 | const networkDiff = getNetworkDiff(stashedChanges) |
| 747 | if (!networkDiff) return |
| 748 | const speculativeChanges = this.store.filterChangesByScope( |
| 749 | this.store.extractingChanges(() => { |
no test coverage detected