From f4a1999a8beec46103ecc6f3512e3b0e3d024856 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Wed, 28 Jun 2023 19:25:16 +0200 Subject: [PATCH] fix(communication): send ready event when both sides are ready Signed-off-by: Tilman Vatteroth --- .../realtime-user-status-adapter.spec.ts | 20 ++- .../backend-websocket-adapter.spec.ts | 4 +- .../realtime/websocket/websocket.gateway.ts | 2 +- .../message-transporter.ts | 67 ++++++-- commons/src/message-transporters/message.ts | 3 +- .../mocked-backend-transport-adapter.ts | 28 ++-- ...n-memory-connection-message.transporter.ts | 52 ------ .../in-memory-connection-transport-adapter.ts | 77 +++++++++ .../src/y-doc-sync/y-doc-sync-adapter.spec.ts | 154 ++++++++++++------ .../editor-page/editor-pane/editor-pane.tsx | 2 +- .../yjs/frontend-websocket-adapter.spec.ts | 4 +- 11 files changed, 271 insertions(+), 142 deletions(-) delete mode 100644 commons/src/y-doc-sync/in-memory-connection-message.transporter.ts create mode 100644 commons/src/y-doc-sync/in-memory-connection-transport-adapter.ts diff --git a/backend/src/realtime/realtime-note/realtime-user-status-adapter.spec.ts b/backend/src/realtime/realtime-note/realtime-user-status-adapter.spec.ts index b2dd601a1..192e37349 100644 --- a/backend/src/realtime/realtime-note/realtime-user-status-adapter.spec.ts +++ b/backend/src/realtime/realtime-note/realtime-user-status-adapter.spec.ts @@ -8,7 +8,6 @@ import { MessageTransporter, MessageType, MockedBackendTransportAdapter, - waitForOtherPromisesToFinish, } from '@hedgedoc/commons'; import { RealtimeUserStatusAdapter } from './realtime-user-status-adapter'; @@ -41,6 +40,14 @@ describe('realtime user status adapter', () => { let messageTransporterNotReady: MessageTransporter; let messageTransporterDecline: MessageTransporter; + beforeAll(() => { + jest.useFakeTimers(); + }); + + afterAll(() => { + jest.useRealTimers(); + }); + beforeEach(async () => { clientLoggedIn1 = undefined; clientLoggedIn2 = undefined; @@ -139,11 +146,12 @@ describe('realtime user status adapter', () => { 'sendMessage', ); - messageTransporterLoggedIn1.sendReady(); - messageTransporterLoggedIn2.sendReady(); - messageTransporterGuest.sendReady(); - messageTransporterDecline.sendReady(); - await waitForOtherPromisesToFinish(); + messageTransporterLoggedIn1.startSendingOfReadyRequests(); + messageTransporterLoggedIn2.startSendingOfReadyRequests(); + messageTransporterGuest.startSendingOfReadyRequests(); + messageTransporterDecline.startSendingOfReadyRequests(); + + jest.advanceTimersByTime(500); }); it('can answer a state request', () => { diff --git a/backend/src/realtime/websocket/backend-websocket-adapter.spec.ts b/backend/src/realtime/websocket/backend-websocket-adapter.spec.ts index 7cb882e85..4bae2cf4c 100644 --- a/backend/src/realtime/websocket/backend-websocket-adapter.spec.ts +++ b/backend/src/realtime/websocket/backend-websocket-adapter.spec.ts @@ -102,9 +102,9 @@ describe('backend websocket adapter', () => { }); it('can send messages', () => { - const value: Message = { type: MessageType.READY }; + const value: Message = { type: MessageType.READY_REQUEST }; sut.send(value); - expect(mockedSocket.send).toHaveBeenCalledWith('{"type":"READY"}'); + expect(mockedSocket.send).toHaveBeenCalledWith('{"type":"READY_REQUEST"}'); }); it('can read the connection state when open', () => { diff --git a/backend/src/realtime/websocket/websocket.gateway.ts b/backend/src/realtime/websocket/websocket.gateway.ts index 8a72bac9e..9750d7c8a 100644 --- a/backend/src/realtime/websocket/websocket.gateway.ts +++ b/backend/src/realtime/websocket/websocket.gateway.ts @@ -106,7 +106,7 @@ export class WebsocketGateway implements OnGatewayConnection { realtimeNote.addClient(connection); - websocketTransporter.sendReady(); + websocketTransporter.startSendingOfReadyRequests(); } catch (error: unknown) { this.logger.error( `Error occurred while initializing: ${(error as Error).message}`, diff --git a/commons/src/message-transporters/message-transporter.ts b/commons/src/message-transporters/message-transporter.ts index c4a71c21f..5a01a9602 100644 --- a/commons/src/message-transporters/message-transporter.ts +++ b/commons/src/message-transporters/message-transporter.ts @@ -7,7 +7,7 @@ import { Message, MessagePayloads, MessageType } from './message.js' import { TransportAdapter } from './transport-adapter.js' import { EventEmitter2, Listener } from 'eventemitter2' -export type MessageEvents = MessageType | 'connected' | 'disconnected' +export type MessageEvents = MessageType | 'connected' | 'disconnected' | 'ready' type MessageEventPayloadMap = { [E in MessageEvents]: E extends keyof MessagePayloads @@ -26,11 +26,13 @@ export enum ConnectionState { */ export class MessageTransporter extends EventEmitter2 { private transportAdapter: TransportAdapter | undefined - private readyMessageReceived = false private destroyOnMessageEventHandler: undefined | (() => void) private destroyOnErrorEventHandler: undefined | (() => void) private destroyOnCloseEventHandler: undefined | (() => void) private destroyOnConnectedEventHandler: undefined | (() => void) + private thisSideReady = false + private otherSideReady = false + private readyInterval: NodeJS.Timer | undefined public sendMessage(content: Message): void { if (!this.isConnected()) { @@ -59,6 +61,8 @@ export class MessageTransporter extends EventEmitter2 { throw new Error('Websocket must be connected') } this.unbindEventsFromPreviousWebsocket() + this.thisSideReady = false + this.otherSideReady = false this.transportAdapter = websocket this.bindWebsocketEvents(websocket) @@ -72,14 +76,41 @@ export class MessageTransporter extends EventEmitter2 { } protected receiveMessage(message: Message): void { - if (message.type === MessageType.READY) { - this.readyMessageReceived = true + if (!this.thisSideReady) { + return + } + if (message.type === MessageType.READY_REQUEST) { + this.sendMessage({ type: MessageType.READY_ANSWER }) + return + } + if (message.type === MessageType.READY_ANSWER) { + this.processReadyAnswer() + return + } + if (!this.isReady()) { + return } this.emit(message.type, message) } + private processReadyAnswer() { + this.stopSendingOfReadyRequests() + if (this.otherSideReady) { + return + } + this.otherSideReady = true + this.emit('ready') + } + + private stopSendingOfReadyRequests() { + if (this.readyInterval !== undefined) { + clearInterval(this.readyInterval) + } + } + public disconnect(): void { this.transportAdapter?.disconnect() + this.onDisconnecting() } public getConnectionState(): ConnectionState { @@ -123,9 +154,11 @@ export class MessageTransporter extends EventEmitter2 { if (this.transportAdapter === undefined) { return } + this.stopSendingOfReadyRequests() this.unbindEventsFromPreviousWebsocket() + this.thisSideReady = false + this.otherSideReady = false this.transportAdapter = undefined - this.readyMessageReceived = false this.emit('disconnected') } @@ -137,10 +170,10 @@ export class MessageTransporter extends EventEmitter2 { } /** - * Indicates if the message transporter has receives a {@link MessageType.READY ready message} yet. + * Indicates if the message transporter is ready to receives messages. */ public isReady(): boolean { - return this.readyMessageReceived + return this.thisSideReady && this.otherSideReady } /** @@ -151,10 +184,10 @@ export class MessageTransporter extends EventEmitter2 { * @return The event listener that waits for ready messages */ public doAsSoonAsReady(callback: () => void): Listener { - if (this.readyMessageReceived) { + if (this.isReady()) { callback() } - return this.on(MessageType.READY, callback, { + return this.on('ready', callback, { objectify: true }) as Listener } @@ -175,9 +208,17 @@ export class MessageTransporter extends EventEmitter2 { }) as Listener } - public sendReady(): void { - this.sendMessage({ - type: MessageType.READY - }) + /** + * Marks the transporter as ready for communication and starts sending of ready requests to the other side. + * This method should be called after all preparations are done and messages can be processed. + */ + public startSendingOfReadyRequests(): void { + this.thisSideReady = true + + this.readyInterval = setInterval(() => { + this.sendMessage({ + type: MessageType.READY_REQUEST + }) + }, 100) } } diff --git a/commons/src/message-transporters/message.ts b/commons/src/message-transporters/message.ts index a93c394d4..76fd22c47 100644 --- a/commons/src/message-transporters/message.ts +++ b/commons/src/message-transporters/message.ts @@ -18,7 +18,8 @@ export enum MessageType { REALTIME_USER_STATE_REQUEST = 'REALTIME_USER_STATE_REQUEST', REALTIME_USER_SET_ACTIVITY = 'REALTIME_USER_SET_ACTIVITY', - READY = 'READY' + READY_REQUEST = 'READY_REQUEST', + READY_ANSWER = 'READY_ANSWER' } export interface MessagePayloads { diff --git a/commons/src/message-transporters/mocked-backend-transport-adapter.ts b/commons/src/message-transporters/mocked-backend-transport-adapter.ts index dc46307f6..1d824368b 100644 --- a/commons/src/message-transporters/mocked-backend-transport-adapter.ts +++ b/commons/src/message-transporters/mocked-backend-transport-adapter.ts @@ -70,18 +70,22 @@ export class MockedBackendTransportAdapter implements TransportAdapter { send(value: Message): void { if (value.type === MessageType.NOTE_CONTENT_STATE_REQUEST) { - new Promise(() => { - this.messageHandler?.({ - type: MessageType.NOTE_CONTENT_UPDATE, - payload: this.doc.encodeStateAsUpdate(value.payload) - }) - }).catch((error: Error) => console.error(error)) - } else if (value.type === MessageType.READY) { - new Promise(() => { - this.messageHandler?.({ - type: MessageType.READY - }) - }).catch((error: Error) => console.error(error)) + setTimeout( + () => + this.messageHandler?.({ + type: MessageType.NOTE_CONTENT_UPDATE, + payload: this.doc.encodeStateAsUpdate(value.payload) + }), + 0 + ) + } else if (value.type === MessageType.READY_REQUEST) { + setTimeout( + () => + this.messageHandler?.({ + type: MessageType.READY_ANSWER + }), + 0 + ) } } } diff --git a/commons/src/y-doc-sync/in-memory-connection-message.transporter.ts b/commons/src/y-doc-sync/in-memory-connection-message.transporter.ts deleted file mode 100644 index 2fee2476e..000000000 --- a/commons/src/y-doc-sync/in-memory-connection-message.transporter.ts +++ /dev/null @@ -1,52 +0,0 @@ -/* - * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) - * - * SPDX-License-Identifier: AGPL-3.0-only - */ -import { - ConnectionState, - MessageTransporter -} from '../message-transporters/message-transporter.js' -import { Message, MessageType } from '../message-transporters/message.js' - -/** - * Message transporter for testing purposes that redirects message to another in memory connection message transporter instance. - */ -export class InMemoryConnectionMessageTransporter extends MessageTransporter { - private otherSide: InMemoryConnectionMessageTransporter | undefined - - constructor(private name: string) { - super() - } - - public connect(other: InMemoryConnectionMessageTransporter): void { - this.otherSide = other - other.otherSide = this - this.onConnected() - other.onConnected() - } - - public disconnect(): void { - this.onDisconnecting() - - if (this.otherSide) { - this.otherSide.onDisconnecting() - this.otherSide.otherSide = undefined - this.otherSide = undefined - } - } - - sendMessage(content: Message): void { - if (this.otherSide === undefined) { - throw new Error('Disconnected') - } - console.debug(`${this.name}`, 'Sending', content) - this.otherSide?.receiveMessage(content) - } - - getConnectionState(): ConnectionState { - return this.otherSide !== undefined - ? ConnectionState.CONNECTED - : ConnectionState.DISCONNECTED - } -} diff --git a/commons/src/y-doc-sync/in-memory-connection-transport-adapter.ts b/commons/src/y-doc-sync/in-memory-connection-transport-adapter.ts new file mode 100644 index 000000000..8dd7478a3 --- /dev/null +++ b/commons/src/y-doc-sync/in-memory-connection-transport-adapter.ts @@ -0,0 +1,77 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ +import { TransportAdapter } from '../message-transporters/index.js' +import { ConnectionState } from '../message-transporters/index.js' +import { Message, MessageType } from '../message-transporters/message.js' + +/** + * Message transporter for testing purposes that redirects message to another in memory connection message transporter instance. + */ +export class InMemoryConnectionTransportAdapter implements TransportAdapter { + private otherSide: InMemoryConnectionTransportAdapter | undefined + + private onCloseHandler: undefined | (() => void) + private onConnectedHandler: undefined | (() => void) + private onErrorHandler: undefined | (() => void) + private onMessageHandler: undefined | ((value: Message) => void) + + constructor(private name: string) {} + + getConnectionState(): ConnectionState { + return this.otherSide !== undefined + ? ConnectionState.CONNECTED + : ConnectionState.DISCONNECTED + } + + bindOnCloseEvent(handler: () => void): () => void { + this.onCloseHandler = handler + return () => (this.onCloseHandler = undefined) + } + + bindOnConnectedEvent(handler: () => void): () => void { + this.onConnectedHandler = handler + return () => (this.onConnectedHandler = undefined) + } + + bindOnErrorEvent(handler: () => void): () => void { + this.onErrorHandler = handler + return () => (this.onErrorHandler = undefined) + } + + bindOnMessageEvent( + handler: (value: Message) => void + ): () => void { + this.onMessageHandler = handler + return () => (this.onMessageHandler = undefined) + } + + receiveMessage(content: Message): void { + this.onMessageHandler?.(content) + } + + send(content: Message): void { + if (this.otherSide === undefined) { + throw new Error('Disconnected') + } + console.debug(`${this.name}`, 'Sending', content) + this.otherSide?.receiveMessage(content) + } + + public connect(other: InMemoryConnectionTransportAdapter): void { + this.otherSide = other + other.otherSide = this + } + + disconnect(): void { + this.onCloseHandler?.() + + if (this.otherSide) { + this.otherSide.onCloseHandler?.() + this.otherSide.otherSide = undefined + this.otherSide = undefined + } + } +} diff --git a/commons/src/y-doc-sync/y-doc-sync-adapter.spec.ts b/commons/src/y-doc-sync/y-doc-sync-adapter.spec.ts index 676bbf51d..a2ae091b7 100644 --- a/commons/src/y-doc-sync/y-doc-sync-adapter.spec.ts +++ b/commons/src/y-doc-sync/y-doc-sync-adapter.spec.ts @@ -3,14 +3,22 @@ * * SPDX-License-Identifier: AGPL-3.0-only */ +import { MessageTransporter } from '../message-transporters/index.js' import { Message, MessageType } from '../message-transporters/message.js' -import { InMemoryConnectionMessageTransporter } from './in-memory-connection-message.transporter.js' +import { InMemoryConnectionTransportAdapter } from './in-memory-connection-transport-adapter.js' import { RealtimeDoc } from './realtime-doc.js' import { YDocSyncClientAdapter } from './y-doc-sync-client-adapter.js' import { YDocSyncServerAdapter } from './y-doc-sync-server-adapter.js' -import { describe, expect, it } from '@jest/globals' +import { describe, expect, it, beforeAll, jest, afterAll } from '@jest/globals' + +describe('y-doc-sync-adapter', () => { + beforeAll(() => { + jest.useFakeTimers() + }) + afterAll(() => { + jest.useRealTimers() + }) -describe('message transporter', () => { it('server client communication', async () => { const docServer: RealtimeDoc = new RealtimeDoc('This is a test note') const docClient1: RealtimeDoc = new RealtimeDoc() @@ -21,51 +29,76 @@ describe('message transporter', () => { const textClient2 = docClient2.getMarkdownContentChannel() textServer.observe(() => - console.debug('textServer', new Date(), textServer.toString()) + console.log('textServer', new Date(), textServer.toString()) ) textClient1.observe(() => - console.debug('textClient1', new Date(), textClient1.toString()) + console.log('textClient1', new Date(), textClient1.toString()) ) textClient2.observe(() => - console.debug('textClient2', new Date(), textClient2.toString()) + console.log('textClient2', new Date(), textClient2.toString()) ) - const transporterServerTo1 = new InMemoryConnectionMessageTransporter('s>1') - const transporterServerTo2 = new InMemoryConnectionMessageTransporter('s>2') - const transporterClient1 = new InMemoryConnectionMessageTransporter('1>s') - const transporterClient2 = new InMemoryConnectionMessageTransporter('2>s') + const transporterAdapterServerTo1 = new InMemoryConnectionTransportAdapter( + 's>1' + ) + const transporterAdapterServerTo2 = new InMemoryConnectionTransportAdapter( + 's>2' + ) + const transporterAdapterClient1 = new InMemoryConnectionTransportAdapter( + '1>s' + ) + const transporterAdapterClient2 = new InMemoryConnectionTransportAdapter( + '2>s' + ) - transporterServerTo1.on(MessageType.NOTE_CONTENT_UPDATE, () => - console.debug('Received NOTE_CONTENT_UPDATE from client 1 to server') + const messageTransporterServerTo1 = new MessageTransporter() + const messageTransporterServerTo2 = new MessageTransporter() + const messageTransporterClient1 = new MessageTransporter() + const messageTransporterClient2 = new MessageTransporter() + + messageTransporterServerTo1.on(MessageType.NOTE_CONTENT_UPDATE, () => + console.log('Received NOTE_CONTENT_UPDATE from client 1 to server') ) - transporterServerTo2.on(MessageType.NOTE_CONTENT_UPDATE, () => - console.debug('Received NOTE_CONTENT_UPDATE from client 2 to server') + messageTransporterServerTo2.on(MessageType.NOTE_CONTENT_UPDATE, () => + console.log('Received NOTE_CONTENT_UPDATE from client 2 to server') ) - transporterClient1.on(MessageType.NOTE_CONTENT_UPDATE, () => - console.debug('Received NOTE_CONTENT_UPDATE from server to client 1') + messageTransporterClient1.on(MessageType.NOTE_CONTENT_UPDATE, () => + console.log('Received NOTE_CONTENT_UPDATE from server to client 1') ) - transporterClient2.on(MessageType.NOTE_CONTENT_UPDATE, () => - console.debug('Received NOTE_CONTENT_UPDATE from server to client 2') + messageTransporterClient2.on(MessageType.NOTE_CONTENT_UPDATE, () => + console.log('Received NOTE_CONTENT_UPDATE from server to client 2') ) - transporterServerTo1.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => - console.debug('Received NOTE_CONTENT_REQUEST from client 1 to server') + messageTransporterServerTo1.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => + console.log('Received NOTE_CONTENT_REQUEST from client 1 to server') ) - transporterServerTo2.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => - console.debug('Received NOTE_CONTENT_REQUEST from client 2 to server') + messageTransporterServerTo2.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => + console.log('Received NOTE_CONTENT_REQUEST from client 2 to server') ) - transporterClient1.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => - console.debug('Received NOTE_CONTENT_REQUEST from server to client 1') + messageTransporterClient1.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => + console.log('Received NOTE_CONTENT_REQUEST from server to client 1') ) - transporterClient2.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => - console.debug('Received NOTE_CONTENT_REQUEST from server to client 2') + messageTransporterClient2.on(MessageType.NOTE_CONTENT_STATE_REQUEST, () => + console.log('Received NOTE_CONTENT_REQUEST from server to client 2') ) - transporterClient1.on('connected', () => console.debug('1>s is connected')) - transporterClient2.on('connected', () => console.debug('2>s is connected')) - transporterServerTo1.on('connected', () => - console.debug('s>1 is connected') + messageTransporterClient1.doAsSoonAsConnected(() => + console.log('1>s is connected') ) - transporterServerTo2.on('connected', () => - console.debug('s>2 is connected') + messageTransporterClient2.doAsSoonAsConnected(() => + console.log('2>s is connected') + ) + messageTransporterServerTo1.doAsSoonAsConnected(() => + console.log('s>1 is connected') + ) + messageTransporterServerTo2.doAsSoonAsConnected(() => + console.log('s>2 is connected') + ) + messageTransporterClient1.doAsSoonAsReady(() => console.log('1>s is ready')) + messageTransporterClient2.doAsSoonAsReady(() => console.log('2>s is ready')) + messageTransporterServerTo1.doAsSoonAsReady(() => + console.log('s>1 is connected') + ) + messageTransporterServerTo2.doAsSoonAsReady(() => + console.log('s>2 is connected') ) docServer.on('update', (update: number[], origin: unknown) => { @@ -73,74 +106,91 @@ describe('message transporter', () => { type: MessageType.NOTE_CONTENT_UPDATE, payload: update } - if (origin !== transporterServerTo1) { - console.debug('YDoc on Server updated. Sending to Client 1') - transporterServerTo1.sendMessage(message) + if (origin !== messageTransporterServerTo1) { + console.log('YDoc on Server updated. Sending to Client 1') + messageTransporterServerTo1.sendMessage(message) } - if (origin !== transporterServerTo2) { - console.debug('YDoc on Server updated. Sending to Client 2') - transporterServerTo2.sendMessage(message) + if (origin !== messageTransporterServerTo2) { + console.log('YDoc on Server updated. Sending to Client 2') + messageTransporterServerTo2.sendMessage(message) } }) docClient1.on('update', (update: number[], origin: unknown) => { - if (origin !== transporterClient1) { - console.debug('YDoc on client 1 updated. Sending to Server') + if (origin !== messageTransporterClient1) { + console.log('YDoc on client 1 updated. Sending to Server') } }) docClient2.on('update', (update: number[], origin: unknown) => { - if (origin !== transporterClient2) { - console.debug('YDoc on client 2 updated. Sending to Server') + if (origin !== messageTransporterClient2) { + console.log('YDoc on client 2 updated. Sending to Server') } }) const yDocSyncAdapter1 = new YDocSyncClientAdapter( - transporterClient1, + messageTransporterClient1, docClient1 ) const yDocSyncAdapter2 = new YDocSyncClientAdapter( - transporterClient2, + messageTransporterClient2, docClient2 ) const yDocSyncAdapterServerTo1 = new YDocSyncServerAdapter( - transporterServerTo1, + messageTransporterServerTo1, docServer, () => true ) const yDocSyncAdapterServerTo2 = new YDocSyncServerAdapter( - transporterServerTo2, + messageTransporterServerTo2, docServer, () => true ) const waitForClient1Sync = new Promise((resolve) => { yDocSyncAdapter1.doAsSoonAsSynced(() => { - console.debug('client 1 received the first sync') + console.log('client 1 received the first sync') resolve() }) }) const waitForClient2Sync = new Promise((resolve) => { yDocSyncAdapter2.doAsSoonAsSynced(() => { - console.debug('client 2 received the first sync') + console.log('client 2 received the first sync') resolve() }) }) const waitForServerTo11Sync = new Promise((resolve) => { yDocSyncAdapterServerTo1.doAsSoonAsSynced(() => { - console.debug('server 1 received the first sync') + console.log('server 1 received the first sync') resolve() }) }) const waitForServerTo21Sync = new Promise((resolve) => { yDocSyncAdapterServerTo2.doAsSoonAsSynced(() => { - console.debug('server 2 received the first sync') + console.log('server 2 received the first sync') resolve() }) }) - transporterClient1.connect(transporterServerTo1) - transporterClient2.connect(transporterServerTo2) + transporterAdapterClient1.connect(transporterAdapterServerTo1) + transporterAdapterClient2.connect(transporterAdapterServerTo2) + + messageTransporterClient1.setAdapter(transporterAdapterClient1) + messageTransporterClient2.setAdapter(transporterAdapterClient2) + messageTransporterServerTo1.setAdapter(transporterAdapterServerTo1) + messageTransporterServerTo2.setAdapter(transporterAdapterServerTo2) + + messageTransporterClient1.startSendingOfReadyRequests() + messageTransporterClient2.startSendingOfReadyRequests() + messageTransporterServerTo1.startSendingOfReadyRequests() + messageTransporterServerTo2.startSendingOfReadyRequests() + + jest.advanceTimersByTime(1000) + + expect(messageTransporterClient1.isReady()).toBeTruthy() + expect(messageTransporterClient2.isReady()).toBeTruthy() + expect(messageTransporterServerTo1.isReady()).toBeTruthy() + expect(messageTransporterServerTo2.isReady()).toBeTruthy() yDocSyncAdapter1.requestDocumentState() yDocSyncAdapter2.requestDocumentState() diff --git a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx index a00b429b2..d26852762 100644 --- a/frontend/src/components/editor-page/editor-pane/editor-pane.tsx +++ b/frontend/src/components/editor-page/editor-pane/editor-pane.tsx @@ -135,7 +135,7 @@ export const EditorPane: React.FC = ({ scrollState, onScroll, o const mayEdit = useMayEdit() useEffect(() => { - const listener = messageTransporter.doAsSoonAsConnected(() => messageTransporter.sendReady()) + const listener = messageTransporter.doAsSoonAsConnected(() => messageTransporter.startSendingOfReadyRequests()) return () => { listener.off() } diff --git a/frontend/src/components/editor-page/editor-pane/hooks/yjs/frontend-websocket-adapter.spec.ts b/frontend/src/components/editor-page/editor-pane/hooks/yjs/frontend-websocket-adapter.spec.ts index 9ba461b96..57c747567 100644 --- a/frontend/src/components/editor-page/editor-pane/hooks/yjs/frontend-websocket-adapter.spec.ts +++ b/frontend/src/components/editor-page/editor-pane/hooks/yjs/frontend-websocket-adapter.spec.ts @@ -89,9 +89,9 @@ describe('frontend websocket', () => { it('can send messages', () => { mockSocket() - const value: Message = { type: MessageType.READY } + const value: Message = { type: MessageType.READY_REQUEST } adapter.send(value) - expect(sendSpy).toHaveBeenCalledWith('{"type":"READY"}') + expect(sendSpy).toHaveBeenCalledWith('{"type":"READY_REQUEST"}') }) it('can read the connection state when open', () => {