Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2023-07-16 21:27:32 +02:00
parent 4d70ccafbc
commit 5f27996ed0
No known key found for this signature in database
GPG key ID: B97799103358209B
5 changed files with 79 additions and 30 deletions

View file

@ -0,0 +1,27 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useMemo } from 'react'
import { EditorView } from '@codemirror/view'
import { setNoteContent } from '../../../../redux/note-details/methods'
/**
* Syncs the CodeMirror content to the redux store.
*
* @return the codemirror extension that updates the redux state
*/
export const useSyncToReduxExtension = () => {
return useMemo(
() =>
EditorView.updateListener.of((update) => {
if (!update.docChanged) {
return
}
setNoteContent(update.state.sliceDoc())
}),
[]
)
}

View file

@ -24,7 +24,6 @@ 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'
import { useOnMetadataUpdated } from './hooks/yjs/use-on-metadata-updated'
import { useOnNoteDeleted } from './hooks/yjs/use-on-note-deleted'
@ -43,6 +42,7 @@ import { lintGutter } from '@codemirror/lint'
import { oneDark } from '@codemirror/theme-one-dark'
import ReactCodeMirror from '@uiw/react-codemirror'
import React, { useEffect, useMemo } from 'react'
import { useSyncToReduxExtension } from './codemirror-extensions/use-sync-to-redux-extension'
export type EditorPaneProps = ScrollProps
@ -83,12 +83,14 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
useOnMetadataUpdated(messageTransporter)
useOnNoteDeleted(messageTransporter)
useBindYTextToRedux(realtimeDoc)
useReceiveRealtimeUsers(messageTransporter)
useSendRealtimeActivity(messageTransporter)
const syncToReduxExtension = useSyncToReduxExtension()
const extensions = useMemo(
() => [
syncToReduxExtension,
linterExtension,
lintGutter(),
markdown({
@ -107,17 +109,18 @@ export const EditorPane: React.FC<EditorPaneProps> = ({ scrollState, onScroll, o
spellCheckExtension
],
[
syncToReduxExtension,
linterExtension,
remoteCursorsExtension,
autoCompletionExtension,
lineWrappingExtension,
editorScrollExtension,
tablePasteExtensions,
fileInsertExtension,
autoCompletionExtension,
cursorActivityExtension,
updateViewContextExtension,
yjsExtension,
spellCheckExtension,
lineWrappingExtension
spellCheckExtension
]
)

View file

@ -1,22 +0,0 @@
/*
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { setNoteContent } from '../../../../../redux/note-details/methods'
import type { RealtimeDoc } from '@hedgedoc/commons'
import { useEffect } from 'react'
/**
* One-Way-synchronizes the text of the markdown content channel from the given {@link RealtimeDoc realtime doc} into the global application state.
*
* @param realtimeDoc The {@link RealtimeDoc realtime doc} that contains the markdown content
*/
export const useBindYTextToRedux = (realtimeDoc: RealtimeDoc): void => {
useEffect(() => {
const yText = realtimeDoc.getMarkdownContentChannel()
const yTextCallback = () => setNoteContent(yText.toString())
yText.observe(yTextCallback)
return () => yText.unobserve(yTextCallback)
}, [realtimeDoc])
}

View file

@ -0,0 +1,39 @@
/*
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useCallback, useEffect, useRef, useState } from 'react'
import { useInterval } from 'react-use'
/**
* Takes a value that changes often and outputs the last value that hasn't been changed in the last interval.
*
* @param value The value to defer
* @param initialValue The initial value that is used until the first update
* @param checkInterval The interval in ms that is used to check for updates. Default is 200ms.
* @return The slowed down value
*/
export const useDeferredState = <T>(value: T, initialValue: T, checkInterval = 200): T => {
const valueRef = useRef<T>(initialValue)
const lastTimestamp = useRef<number>(0)
const [deferredValue, setDeferredValue] = useState<T>(initialValue)
useEffect(() => {
valueRef.current = value
lastTimestamp.current = new Date().getTime()
}, [value])
useInterval(
useCallback(() => {
const currentTimeStamp = new Date().getTime()
if (currentTimeStamp - lastTimestamp.current >= checkInterval) {
setDeferredValue(valueRef.current)
}
}, [checkInterval]),
checkInterval
)
return deferredValue
}

View file

@ -6,6 +6,7 @@
import { useFrontendConfig } from '../../components/common/frontend-config-context/use-frontend-config'
import { useApplicationState } from './use-application-state'
import { useMemo } from 'react'
import { useDeferredState } from './use-deferred-state'
/**
* Returns the markdown content from the global application state trimmed to the maximal note length and without the frontmatter lines.
@ -28,7 +29,8 @@ export const useTrimmedNoteMarkdownContentWithoutFrontmatter = (): string[] => {
}
}, [markdownContent, maxLength])
return useMemo(() => {
return trimmedLines.slice(lineOffset)
}, [lineOffset, trimmedLines])
return useDeferredState(
useMemo(() => trimmedLines.slice(lineOffset), [lineOffset, trimmedLines]),
[]
)
}