feat(realtime): add disconnect reason

The frontend now doesn't try to reconnect, when the disconnection happened because of a lack of permissions

Signed-off-by: Philip Molares <philip.molares@udo.edu>
This commit is contained in:
Philip Molares 2023-10-22 21:33:34 +02:00
parent f6cfe74d8c
commit 723f3f611c
11 changed files with 111 additions and 46 deletions

View file

@ -5,7 +5,7 @@
*/
import { FrontendWebsocketAdapter } from './frontend-websocket-adapter'
import type { Message } from '@hedgedoc/commons'
import { ConnectionState, MessageType } from '@hedgedoc/commons'
import { ConnectionState, DisconnectReason, MessageType } from '@hedgedoc/commons'
import { Mock } from 'ts-mockery'
describe('frontend websocket', () => {
@ -34,11 +34,22 @@ describe('frontend websocket', () => {
it('can bind and unbind the close event', () => {
mockSocket()
const handler = jest.fn()
const handler = jest.fn((reason?: DisconnectReason) => console.log(reason))
let modifiedHandler: (event: CloseEvent) => void = jest.fn()
jest.spyOn(mockedSocket, 'addEventListener').mockImplementation((event, handler_) => {
modifiedHandler = handler_
})
const unbind = adapter.bindOnCloseEvent(handler)
expect(addEventListenerSpy).toHaveBeenCalledWith('close', handler)
modifiedHandler(Mock.of<CloseEvent>({ code: DisconnectReason.USER_NOT_PERMITTED }))
expect(handler).toHaveBeenCalledTimes(1)
expect(handler).toHaveBeenCalledWith(DisconnectReason.USER_NOT_PERMITTED)
unbind()
expect(removeEventListenerSpy).toHaveBeenCalledWith('close', handler)
expect(removeEventListenerSpy).toHaveBeenCalled()
})
it('can bind and unbind the connect event', () => {

View file

@ -3,9 +3,8 @@
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { TransportAdapter } from '@hedgedoc/commons'
import type { DisconnectReason, Message, MessageType, TransportAdapter } from '@hedgedoc/commons'
import { ConnectionState } from '@hedgedoc/commons'
import type { Message, MessageType } from '@hedgedoc/commons'
/**
* Implements a transport adapter that communicates using a browser websocket.
@ -13,10 +12,11 @@ import type { Message, MessageType } from '@hedgedoc/commons'
export class FrontendWebsocketAdapter implements TransportAdapter {
constructor(private socket: WebSocket) {}
bindOnCloseEvent(handler: () => void): () => void {
this.socket.addEventListener('close', handler)
bindOnCloseEvent(handler: (reason?: DisconnectReason) => void): () => void {
const callback = (event: CloseEvent): void => handler(event.code)
this.socket.addEventListener('close', callback)
return () => {
this.socket.removeEventListener('close', handler)
this.socket.removeEventListener('close', callback)
}
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -10,7 +10,7 @@ import { Logger } from '../../../../../utils/logger'
import { isMockMode } from '../../../../../utils/test-modes'
import { FrontendWebsocketAdapter } from './frontend-websocket-adapter'
import { useWebsocketUrl } from './use-websocket-url'
import { MessageTransporter, MockedBackendTransportAdapter } from '@hedgedoc/commons'
import { DisconnectReason, MessageTransporter, MockedBackendTransportAdapter } from '@hedgedoc/commons'
import type { Listener } from 'eventemitter2'
import { useCallback, useEffect, useMemo, useRef } from 'react'
@ -28,6 +28,7 @@ export const useRealtimeConnection = (): MessageTransporter => {
const messageTransporter = useMemo(() => new MessageTransporter(), [])
const reconnectCount = useRef(0)
const disconnectReason = useRef<DisconnectReason | undefined>(undefined)
const establishWebsocketConnection = useCallback(() => {
if (isMockMode) {
logger.debug('Creating Loopback connection...')
@ -57,7 +58,7 @@ export const useRealtimeConnection = (): MessageTransporter => {
const isConnected = useApplicationState((state) => state.realtimeStatus.isConnected)
useEffect(() => {
if (isConnected || reconnectCount.current > 0) {
if (isConnected || reconnectCount.current > 0 || disconnectReason.current === DisconnectReason.USER_NOT_PERMITTED) {
return
}
establishWebsocketConnection()
@ -86,9 +87,16 @@ export const useRealtimeConnection = (): MessageTransporter => {
useEffect(() => {
const connectedListener = messageTransporter.doAsSoonAsReady(() => setRealtimeConnectionState(true))
const disconnectedListener = messageTransporter.on('disconnected', () => setRealtimeConnectionState(false), {
objectify: true
}) as Listener
const disconnectedListener = messageTransporter.on(
'disconnected',
(reason?: DisconnectReason) => {
disconnectReason.current = reason
setRealtimeConnectionState(false)
},
{
objectify: true
}
) as Listener
return () => {
connectedListener.off()