mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04:00
refactor: reimplement realtime-communication
This commit refactors a lot of things that are not easy to separate. It replaces the binary protocol of y-protocols with json. It introduces event based message processing. It implements our own code mirror plugins for synchronisation of content and remote cursors Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
67cf1432b2
commit
3a06f84af1
110 changed files with 3920 additions and 2201 deletions
4
frontend/src/redux/application-state.d.ts
vendored
4
frontend/src/redux/application-state.d.ts
vendored
|
@ -8,7 +8,7 @@ import type { HistoryEntryWithOrigin } from '../api/history/types'
|
|||
import type { DarkModeConfig } from './dark-mode/types'
|
||||
import type { EditorConfig } from './editor/types'
|
||||
import type { NoteDetails } from './note-details/types/note-details'
|
||||
import type { RealtimeState } from './realtime/types'
|
||||
import type { RealtimeStatus } from './realtime/types'
|
||||
import type { RendererStatus } from './renderer-status/types'
|
||||
import type { OptionalUserState } from './user/types'
|
||||
|
||||
|
@ -20,5 +20,5 @@ export interface ApplicationState {
|
|||
darkMode: DarkModeConfig
|
||||
noteDetails: NoteDetails
|
||||
rendererStatus: RendererStatus
|
||||
realtime: RealtimeState
|
||||
realtimeStatus: RealtimeStatus
|
||||
}
|
||||
|
|
|
@ -4,33 +4,37 @@
|
|||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { store } from '..'
|
||||
import type { AddOnlineUserAction, OnlineUser, RemoveOnlineUserAction } from './types'
|
||||
import { RealtimeActionType } from './types'
|
||||
import type { SetRealtimeSyncStatusAction, SetRealtimeUsersAction, SetRealtimeConnectionStatusAction } from './types'
|
||||
import { RealtimeStatusActionType } from './types'
|
||||
import type { RealtimeUser } from '@hedgedoc/commons'
|
||||
|
||||
/**
|
||||
* Dispatches an event to add a user
|
||||
*
|
||||
* @param clientId The clientId of the user to add
|
||||
* @param user The user to add.
|
||||
*/
|
||||
export const addOnlineUser = (clientId: number, user: OnlineUser): void => {
|
||||
const action: AddOnlineUserAction = {
|
||||
type: RealtimeActionType.ADD_ONLINE_USER,
|
||||
clientId,
|
||||
user
|
||||
export const setRealtimeUsers = (users: RealtimeUser[]): void => {
|
||||
const action: SetRealtimeUsersAction = {
|
||||
type: RealtimeStatusActionType.SET_REALTIME_USERS,
|
||||
users
|
||||
}
|
||||
store.dispatch(action)
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatches an event to remove a user from the online users list.
|
||||
*
|
||||
* @param clientId The yjs client id of the user to remove from the online users list.
|
||||
*/
|
||||
export const removeOnlineUser = (clientId: number): void => {
|
||||
const action: RemoveOnlineUserAction = {
|
||||
type: RealtimeActionType.REMOVE_ONLINE_USER,
|
||||
clientId
|
||||
}
|
||||
store.dispatch(action)
|
||||
export const setRealtimeConnectionState = (status: boolean): void => {
|
||||
store.dispatch({
|
||||
type: RealtimeStatusActionType.SET_REALTIME_CONNECTION_STATUS,
|
||||
isConnected: status
|
||||
} as SetRealtimeConnectionStatusAction)
|
||||
}
|
||||
|
||||
export const setRealtimeSyncedState = (status: boolean): void => {
|
||||
store.dispatch({
|
||||
type: RealtimeStatusActionType.SET_REALTIME_SYNCED_STATUS,
|
||||
isSynced: status
|
||||
} as SetRealtimeSyncStatusAction)
|
||||
}
|
||||
|
||||
export const resetRealtimeStatus = (): void => {
|
||||
store.dispatch({
|
||||
type: RealtimeStatusActionType.RESET_REALTIME_STATUS
|
||||
})
|
||||
}
|
||||
|
|
|
@ -3,32 +3,45 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { buildStateFromAddUser } from './reducers/build-state-from-add-user'
|
||||
import { buildStateFromRemoveUser } from './reducers/build-state-from-remove-user'
|
||||
import type { RealtimeActions, RealtimeState } from './types'
|
||||
import { RealtimeActionType } from './types'
|
||||
import type { RealtimeStatusActions, RealtimeStatus } from './types'
|
||||
import { RealtimeStatusActionType } from './types'
|
||||
import type { Reducer } from 'redux'
|
||||
|
||||
const initialState: RealtimeState = {
|
||||
users: []
|
||||
const initialState: RealtimeStatus = {
|
||||
isSynced: false,
|
||||
isConnected: false,
|
||||
onlineUsers: []
|
||||
}
|
||||
|
||||
/**
|
||||
* Applies {@link RealtimeReducer realtime actions} to the global application state.
|
||||
* Applies {@link RealtimeStatusReducer realtime actions} to the global application state.
|
||||
*
|
||||
* @param state the current state
|
||||
* @param action the action that should get applied
|
||||
* @return The new changed state
|
||||
*/
|
||||
export const RealtimeReducer: Reducer<RealtimeState, RealtimeActions> = (
|
||||
export const RealtimeStatusReducer: Reducer<RealtimeStatus, RealtimeStatusActions> = (
|
||||
state = initialState,
|
||||
action: RealtimeActions
|
||||
action: RealtimeStatusActions
|
||||
) => {
|
||||
switch (action.type) {
|
||||
case RealtimeActionType.ADD_ONLINE_USER:
|
||||
return buildStateFromAddUser(state, action.clientId, action.user)
|
||||
case RealtimeActionType.REMOVE_ONLINE_USER:
|
||||
return buildStateFromRemoveUser(state, action.clientId)
|
||||
case RealtimeStatusActionType.SET_REALTIME_USERS:
|
||||
return {
|
||||
...state,
|
||||
onlineUsers: action.users
|
||||
}
|
||||
case RealtimeStatusActionType.SET_REALTIME_CONNECTION_STATUS:
|
||||
return {
|
||||
...state,
|
||||
isConnected: action.isConnected
|
||||
}
|
||||
case RealtimeStatusActionType.SET_REALTIME_SYNCED_STATUS:
|
||||
return {
|
||||
...state,
|
||||
isSynced: action.isSynced
|
||||
}
|
||||
case RealtimeStatusActionType.RESET_REALTIME_STATUS:
|
||||
return initialState
|
||||
default:
|
||||
return state
|
||||
}
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { OnlineUser, RealtimeState } from '../types'
|
||||
|
||||
/**
|
||||
* Builds a new {@link RealtimeState} with a new client id that is shown as online.
|
||||
*
|
||||
* @param oldState The old state that will be copied
|
||||
* @param clientId The identifier of the new client
|
||||
* @param user The information about the new user
|
||||
* @return the generated state
|
||||
*/
|
||||
export const buildStateFromAddUser = (oldState: RealtimeState, clientId: number, user: OnlineUser): RealtimeState => {
|
||||
return {
|
||||
users: {
|
||||
...oldState.users,
|
||||
[clientId]: user
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RealtimeState } from '../types'
|
||||
|
||||
/**
|
||||
* Builds a new {@link RealtimeState} but removes the information about a client.
|
||||
*
|
||||
* @param oldState The old state that will be copied
|
||||
* @param clientIdToRemove The identifier of the client that should be removed
|
||||
* @return the generated state
|
||||
*/
|
||||
export const buildStateFromRemoveUser = (oldState: RealtimeState, clientIdToRemove: number): RealtimeState => {
|
||||
const newUsers = { ...oldState.users }
|
||||
delete newUsers[clientIdToRemove]
|
||||
return {
|
||||
users: newUsers
|
||||
}
|
||||
}
|
|
@ -3,38 +3,43 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import type { RealtimeUser } from '@hedgedoc/commons'
|
||||
import type { Action } from 'redux'
|
||||
|
||||
export enum RealtimeActionType {
|
||||
ADD_ONLINE_USER = 'realtime/add-user',
|
||||
REMOVE_ONLINE_USER = 'realtime/remove-user',
|
||||
UPDATE_ONLINE_USER = 'realtime/update-user'
|
||||
export enum RealtimeStatusActionType {
|
||||
SET_REALTIME_USERS = 'realtime/set-users',
|
||||
SET_REALTIME_CONNECTION_STATUS = 'realtime/set-connection-status',
|
||||
SET_REALTIME_SYNCED_STATUS = 'realtime/set-synced-status',
|
||||
RESET_REALTIME_STATUS = 'realtime/reset-realtime-status'
|
||||
}
|
||||
|
||||
export interface RealtimeState {
|
||||
users: Record<number, OnlineUser>
|
||||
export interface SetRealtimeUsersAction extends Action<RealtimeStatusActionType> {
|
||||
type: RealtimeStatusActionType.SET_REALTIME_USERS
|
||||
users: RealtimeUser[]
|
||||
}
|
||||
|
||||
export enum ActiveIndicatorStatus {
|
||||
ACTIVE = 'active',
|
||||
INACTIVE = 'inactive'
|
||||
export interface SetRealtimeConnectionStatusAction extends Action<RealtimeStatusActionType> {
|
||||
type: RealtimeStatusActionType.SET_REALTIME_CONNECTION_STATUS
|
||||
isConnected: boolean
|
||||
}
|
||||
|
||||
export interface OnlineUser {
|
||||
username: string
|
||||
color: string
|
||||
active: ActiveIndicatorStatus
|
||||
export interface SetRealtimeSyncStatusAction extends Action<RealtimeStatusActionType> {
|
||||
type: RealtimeStatusActionType.SET_REALTIME_SYNCED_STATUS
|
||||
isSynced: boolean
|
||||
}
|
||||
|
||||
export interface AddOnlineUserAction extends Action<RealtimeActionType> {
|
||||
type: RealtimeActionType.ADD_ONLINE_USER
|
||||
clientId: number
|
||||
user: OnlineUser
|
||||
export interface ResetRealtimeStatusAction extends Action<RealtimeStatusActionType> {
|
||||
type: RealtimeStatusActionType.RESET_REALTIME_STATUS
|
||||
}
|
||||
|
||||
export interface RemoveOnlineUserAction extends Action<RealtimeActionType> {
|
||||
type: RealtimeActionType.REMOVE_ONLINE_USER
|
||||
clientId: number
|
||||
export interface RealtimeStatus {
|
||||
onlineUsers: RealtimeUser[]
|
||||
isConnected: boolean
|
||||
isSynced: boolean
|
||||
}
|
||||
|
||||
export type RealtimeActions = AddOnlineUserAction | RemoveOnlineUserAction
|
||||
export type RealtimeStatusActions =
|
||||
| SetRealtimeUsersAction
|
||||
| SetRealtimeConnectionStatusAction
|
||||
| SetRealtimeSyncStatusAction
|
||||
| ResetRealtimeStatusAction
|
||||
|
|
|
@ -9,7 +9,7 @@ import { DarkModeConfigReducer } from './dark-mode/reducers'
|
|||
import { EditorConfigReducer } from './editor/reducers'
|
||||
import { HistoryReducer } from './history/reducers'
|
||||
import { NoteDetailsReducer } from './note-details/reducer'
|
||||
import { RealtimeReducer } from './realtime/reducers'
|
||||
import { RealtimeStatusReducer } from './realtime/reducers'
|
||||
import { RendererStatusReducer } from './renderer-status/reducers'
|
||||
import { UserReducer } from './user/reducers'
|
||||
import type { Reducer } from 'redux'
|
||||
|
@ -23,5 +23,5 @@ export const allReducers: Reducer<ApplicationState> = combineReducers<Applicatio
|
|||
darkMode: DarkModeConfigReducer,
|
||||
noteDetails: NoteDetailsReducer,
|
||||
rendererStatus: RendererStatusReducer,
|
||||
realtime: RealtimeReducer
|
||||
realtimeStatus: RealtimeStatusReducer
|
||||
})
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue