feat(realtime): disconnect user on user login status change

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-03-28 17:28:35 +02:00
parent 9b9eafc948
commit 972ec8c9c5
3 changed files with 104 additions and 1 deletions

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
*/
@ -19,6 +19,7 @@ import { useOnImageUploadFromRenderer } from './hooks/image-upload-from-renderer
import { useCodeMirrorTablePasteExtension } from './hooks/table-paste/use-code-mirror-table-paste-extension'
import { useApplyScrollState } from './hooks/use-apply-scroll-state'
import { useCursorActivityCallback } from './hooks/use-cursor-activity-callback'
import { useDisconnectOnUserLoginStatusChange } from './hooks/use-disconnect-on-user-login-status-change'
import { useUpdateCodeMirrorReference } from './hooks/use-update-code-mirror-reference'
import { useBindYTextToRedux } from './hooks/yjs/use-bind-y-text-to-redux'
import { useCodeMirrorYjsExtension } from './hooks/yjs/use-code-mirror-yjs-extension'
@ -56,6 +57,9 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
useApplyScrollState(scrollState)
const messageTransporter = useRealtimeConnection()
useDisconnectOnUserLoginStatusChange(messageTransporter)
const realtimeDoc = useRealtimeDoc()
const editorScrollExtension = useCodeMirrorScrollWatchExtension(onScroll)
const tablePasteExtensions = useCodeMirrorTablePasteExtension()

View file

@ -0,0 +1,70 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import type { LoginUserInfo } from '../../../../api/me/types'
import * as UseApplicationStateModule from '../../../../hooks/common/use-application-state'
import type { ApplicationState } from '../../../../redux/application-state'
import { useDisconnectOnUserLoginStatusChange } from './use-disconnect-on-user-login-status-change'
import type { MessageTransporter } from '@hedgedoc/commons'
import { render } from '@testing-library/react'
import React, { Fragment } from 'react'
import { Mock } from 'ts-mockery'
jest.mock('../../../../hooks/common/use-application-state')
describe('use logout on user change', () => {
const TestComponent: React.FC<{ messageTransporter: MessageTransporter }> = ({ messageTransporter }) => {
useDisconnectOnUserLoginStatusChange(messageTransporter)
return <Fragment />
}
const mockUseApplicationState = (userLoggedIn: boolean) => {
jest
.spyOn(UseApplicationStateModule, 'useApplicationState')
.mockImplementation((fn) =>
fn(Mock.of<ApplicationState>({ user: userLoggedIn ? Mock.of<LoginUserInfo>({}) : null }))
)
}
let disconnectCallback: jest.Mock
let messageTransporter: MessageTransporter
beforeEach(() => {
disconnectCallback = jest.fn()
messageTransporter = Mock.of<MessageTransporter>({ disconnect: disconnectCallback })
})
it("doesn't disconnect if user is logged in before", () => {
mockUseApplicationState(true)
render(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).not.toBeCalled()
})
it("doesn't disconnect if user is not logged in before", () => {
mockUseApplicationState(false)
render(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).not.toBeCalled()
})
it('disconnects if user switches from logged in to logged out', () => {
mockUseApplicationState(true)
const view = render(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).not.toBeCalled()
mockUseApplicationState(false)
view.rerender(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).toBeCalled()
})
it('disconnects if user switches from logged out to logged in', () => {
mockUseApplicationState(false)
const view = render(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).not.toBeCalled()
mockUseApplicationState(true)
view.rerender(<TestComponent messageTransporter={messageTransporter} />)
expect(disconnectCallback).toBeCalled()
})
})

View file

@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useApplicationState } from '../../../../hooks/common/use-application-state'
import type { MessageTransporter } from '@hedgedoc/commons'
import { useEffect, useRef } from 'react'
/**
* Disconnects the given {@link MessageTransporter message transporter} if the user status changes through log-in or log-out.
*
* @param messageTransporter the message transporter to disconnect
*/
export const useDisconnectOnUserLoginStatusChange = (messageTransporter: MessageTransporter): void => {
const previousIsLoggedIn = useRef<boolean | undefined>()
const isLoggedIn = useApplicationState((state) => state.user !== null)
useEffect(() => {
if (previousIsLoggedIn.current === undefined) {
previousIsLoggedIn.current = isLoggedIn
return
}
if (previousIsLoggedIn.current === isLoggedIn) {
return
}
previousIsLoggedIn.current = isLoggedIn
messageTransporter.disconnect()
}, [isLoggedIn, messageTransporter])
}