mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-01-27 06:11:43 +00:00
refactor: extract scroll hooks
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
bded420cab
commit
340adbe69a
4 changed files with 89 additions and 70 deletions
|
@ -3,10 +3,8 @@
|
|||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { useApplicationState } from '../../hooks/common/use-application-state'
|
||||
import { useApplyDarkModeStyle } from '../../hooks/dark-mode/use-apply-dark-mode-style'
|
||||
import { useSaveDarkModePreferenceToLocalStorage } from '../../hooks/dark-mode/use-save-dark-mode-preference-to-local-storage'
|
||||
import { Logger } from '../../utils/logger'
|
||||
import { MotdModal } from '../common/motd-modal/motd-modal'
|
||||
import { CommunicatorImageLightbox } from '../markdown-renderer/extensions/image/communicator-image-lightbox'
|
||||
import { ExtensionEventEmitterProvider } from '../markdown-renderer/hooks/use-extension-event-emitter'
|
||||
|
@ -15,14 +13,14 @@ import { ChangeEditorContentContextProvider } from './change-content-context/cod
|
|||
import { EditorPane } from './editor-pane/editor-pane'
|
||||
import { useComponentsFromAppExtensions } from './editor-pane/hooks/use-components-from-app-extensions'
|
||||
import { HeadMetaProperties } from './head-meta-properties/head-meta-properties'
|
||||
import { useScrollState } from './hooks/use-scroll-state'
|
||||
import { useSetScrollSource } from './hooks/use-set-scroll-source'
|
||||
import { useUpdateLocalHistoryEntry } from './hooks/use-update-local-history-entry'
|
||||
import { RealtimeConnectionAlert } from './realtime-connection-alert/realtime-connection-alert'
|
||||
import { RendererPane } from './renderer-pane/renderer-pane'
|
||||
import { Sidebar } from './sidebar/sidebar'
|
||||
import { Splitter } from './splitter/splitter'
|
||||
import type { DualScrollState, ScrollState } from './synced-scroll/scroll-props'
|
||||
import equal from 'fast-deep-equal'
|
||||
import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
||||
import React, { useMemo, useRef } from 'react'
|
||||
import { useTranslation } from 'react-i18next'
|
||||
|
||||
export enum ScrollSource {
|
||||
|
@ -30,82 +28,31 @@ export enum ScrollSource {
|
|||
RENDERER = 'renderer'
|
||||
}
|
||||
|
||||
const log = new Logger('EditorPage')
|
||||
|
||||
/**
|
||||
* This is the content of the actual editor page.
|
||||
*/
|
||||
export const EditorPageContent: React.FC = () => {
|
||||
useTranslation()
|
||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||
const editorSyncScroll: boolean = useApplicationState((state) => state.editorConfig.syncScroll)
|
||||
|
||||
const [scrollState, setScrollState] = useState<DualScrollState>(() => ({
|
||||
editorScrollState: { firstLineInView: 1, scrolledPercentage: 0 },
|
||||
rendererScrollState: { firstLineInView: 1, scrolledPercentage: 0 }
|
||||
}))
|
||||
|
||||
const onMarkdownRendererScroll = useCallback(
|
||||
(newScrollState: ScrollState) => {
|
||||
if (scrollSource.current === ScrollSource.RENDERER && editorSyncScroll) {
|
||||
setScrollState((old) => {
|
||||
const newState: DualScrollState = {
|
||||
editorScrollState: newScrollState,
|
||||
rendererScrollState: old.rendererScrollState
|
||||
}
|
||||
return equal(newState, old) ? old : newState
|
||||
})
|
||||
}
|
||||
},
|
||||
[editorSyncScroll]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
log.debug('New scroll state', scrollState, scrollSource.current)
|
||||
}, [scrollState])
|
||||
|
||||
const onEditorScroll = useCallback(
|
||||
(newScrollState: ScrollState) => {
|
||||
if (scrollSource.current === ScrollSource.EDITOR && editorSyncScroll) {
|
||||
setScrollState((old) => {
|
||||
const newState: DualScrollState = {
|
||||
rendererScrollState: newScrollState,
|
||||
editorScrollState: old.editorScrollState
|
||||
}
|
||||
return equal(newState, old) ? old : newState
|
||||
})
|
||||
}
|
||||
},
|
||||
[editorSyncScroll]
|
||||
)
|
||||
|
||||
useApplyDarkModeStyle()
|
||||
useSaveDarkModePreferenceToLocalStorage()
|
||||
useUpdateLocalHistoryEntry()
|
||||
|
||||
const setRendererToScrollSource = useCallback(() => {
|
||||
if (scrollSource.current !== ScrollSource.RENDERER) {
|
||||
scrollSource.current = ScrollSource.RENDERER
|
||||
log.debug('Make renderer scroll source')
|
||||
}
|
||||
}, [])
|
||||
|
||||
const setEditorToScrollSource = useCallback(() => {
|
||||
if (scrollSource.current !== ScrollSource.EDITOR) {
|
||||
scrollSource.current = ScrollSource.EDITOR
|
||||
log.debug('Make editor scroll source')
|
||||
}
|
||||
}, [])
|
||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||
const [editorScrollState, onMarkdownRendererScroll] = useScrollState(scrollSource, ScrollSource.EDITOR)
|
||||
const [rendererScrollState, onEditorScroll] = useScrollState(scrollSource, ScrollSource.RENDERER)
|
||||
const setRendererToScrollSource = useSetScrollSource(scrollSource, ScrollSource.RENDERER)
|
||||
const setEditorToScrollSource = useSetScrollSource(scrollSource, ScrollSource.EDITOR)
|
||||
|
||||
const leftPane = useMemo(
|
||||
() => (
|
||||
<EditorPane
|
||||
scrollState={scrollState.editorScrollState}
|
||||
scrollState={editorScrollState}
|
||||
onScroll={onEditorScroll}
|
||||
onMakeScrollSource={setEditorToScrollSource}
|
||||
/>
|
||||
),
|
||||
[onEditorScroll, scrollState.editorScrollState, setEditorToScrollSource]
|
||||
[onEditorScroll, editorScrollState, setEditorToScrollSource]
|
||||
)
|
||||
|
||||
const rightPane = useMemo(
|
||||
|
@ -114,10 +61,10 @@ export const EditorPageContent: React.FC = () => {
|
|||
frameClasses={'h-100 w-100'}
|
||||
onMakeScrollSource={setRendererToScrollSource}
|
||||
onScroll={onMarkdownRendererScroll}
|
||||
scrollState={scrollState.rendererScrollState}
|
||||
scrollState={rendererScrollState}
|
||||
/>
|
||||
),
|
||||
[onMarkdownRendererScroll, scrollState.rendererScrollState, setRendererToScrollSource]
|
||||
[onMarkdownRendererScroll, rendererScrollState, setRendererToScrollSource]
|
||||
)
|
||||
|
||||
const editorExtensionComponents = useComponentsFromAppExtensions()
|
||||
|
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
* 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 { Logger } from '../../../utils/logger'
|
||||
import type { ScrollSource } from '../editor-page-content'
|
||||
import type { ScrollState } from '../synced-scroll/scroll-props'
|
||||
import type { RefObject } from 'react'
|
||||
import { useCallback, useEffect, useState } from 'react'
|
||||
|
||||
const log = new Logger('useScrollState')
|
||||
|
||||
/**
|
||||
* Provides a {@link ScrollState} state and a function that updates the scroll state to a new value.
|
||||
*
|
||||
* @param scrollSourceRef The reference that defines which scroll source is active
|
||||
* @param scrollSource The source for which the state should be created
|
||||
* @return the created scroll state and the update function. The update function accepts only new values if the given scroll source isn't active to prevent callback loops.
|
||||
*/
|
||||
export const useScrollState = (
|
||||
scrollSourceRef: RefObject<ScrollSource>,
|
||||
scrollSource: ScrollSource
|
||||
): [scrollState: ScrollState, onScroll: (newScrollState: ScrollState) => void] => {
|
||||
const editorSyncScroll: boolean = useApplicationState((state) => state.editorConfig.syncScroll)
|
||||
|
||||
const [scrollState, setScrollState] = useState<ScrollState>(() => ({
|
||||
firstLineInView: 1,
|
||||
scrolledPercentage: 0
|
||||
}))
|
||||
|
||||
const onScroll = useCallback(
|
||||
(newScrollState: ScrollState) => {
|
||||
if (scrollSourceRef.current !== scrollSource && editorSyncScroll) {
|
||||
setScrollState(newScrollState)
|
||||
}
|
||||
},
|
||||
[editorSyncScroll, scrollSource, scrollSourceRef]
|
||||
)
|
||||
|
||||
useEffect(() => {
|
||||
log.debug(`New scroll state for ${scrollSource}`, scrollState)
|
||||
}, [scrollSource, scrollState])
|
||||
|
||||
return [scrollState, onScroll]
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file)
|
||||
*
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
import { Logger } from '../../../utils/logger'
|
||||
import type { ScrollSource } from '../editor-page-content'
|
||||
import type { MutableRefObject } from 'react'
|
||||
import { useCallback } from 'react'
|
||||
|
||||
const log = new Logger('useSetScrollSource')
|
||||
|
||||
/**
|
||||
* Provides a function that updates the given {@link ScrollSource} reference to a given value.
|
||||
*
|
||||
* @param scrollSourceReference The reference to update
|
||||
* @param targetScrollSource The value that should be set in the reference
|
||||
* @return The function that sets the reference
|
||||
*/
|
||||
export const useSetScrollSource = (
|
||||
scrollSourceReference: MutableRefObject<ScrollSource>,
|
||||
targetScrollSource: ScrollSource
|
||||
) => {
|
||||
return useCallback(() => {
|
||||
if (scrollSourceReference.current !== targetScrollSource) {
|
||||
scrollSourceReference.current = targetScrollSource
|
||||
log.debug(`Make ${targetScrollSource} scroll source`)
|
||||
}
|
||||
}, [scrollSourceReference, targetScrollSource])
|
||||
}
|
|
@ -16,8 +16,3 @@ export interface ScrollState {
|
|||
firstLineInView: number
|
||||
scrolledPercentage: number
|
||||
}
|
||||
|
||||
export interface DualScrollState {
|
||||
editorScrollState: ScrollState
|
||||
rendererScrollState: ScrollState
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue