feat(renderer): Use callback instead of redux for renderer ready

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2022-10-06 22:48:36 +02:00
parent 285daeef8b
commit 2817740c94
12 changed files with 82 additions and 118 deletions

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -11,6 +11,7 @@ import { DocumentInfobar } from './document-infobar'
import { RenderIframe } from '../editor-page/renderer-pane/render-iframe'
import { RendererType } from '../render-page/window-post-message-communicator/rendering-message'
import { useTrimmedNoteMarkdownContentWithoutFrontmatter } from '../../hooks/common/use-trimmed-note-markdown-content-without-frontmatter'
import { setRendererStatus } from '../../redux/renderer-status/methods'
/**
* Renders the read-only version of a note with a header bar that contains information about the note.
@ -29,6 +30,7 @@ export const DocumentReadOnlyPageContent: React.FC = () => {
markdownContentLines={markdownContentLines}
onFirstHeadingChange={updateNoteTitleByFirstHeading}
rendererType={RendererType.DOCUMENT}
onRendererStatusChange={setRendererStatus}
/>
</Fragment>
)

View file

@ -5,7 +5,7 @@
*/
import type { PropsWithChildren } from 'react'
import React, { useCallback, useState } from 'react'
import React, { useCallback, useEffect, useState } from 'react'
import { Trans, useTranslation } from 'react-i18next'
import { ShowIf } from '../../../common/show-if/show-if'
import { NoteInfoLine } from './note-info-line'
@ -14,8 +14,8 @@ import { useEditorToRendererCommunicator } from '../../render-context/editor-to-
import type { OnWordCountCalculatedMessage } from '../../../render-page/window-post-message-communicator/rendering-message'
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
import { useEditorReceiveHandler } from '../../../render-page/window-post-message-communicator/hooks/use-editor-receive-handler'
import { useEffectOnRendererReady } from '../../../render-page/window-post-message-communicator/hooks/use-effect-on-renderer-ready'
import { cypressId } from '../../../../utils/cypress-attribute'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
/**
* Creates a new info line for the document information dialog that holds the
@ -31,11 +31,12 @@ export const NoteInfoLineWordCount: React.FC<PropsWithChildren<unknown>> = () =>
useCallback((values: OnWordCountCalculatedMessage) => setWordCount(values.words), [setWordCount])
)
useEffectOnRendererReady(
useCallback(() => {
const rendererReady = useApplicationState((state) => state.rendererStatus.rendererReady)
useEffect(() => {
if (rendererReady) {
editorToRendererCommunicator.sendMessageToOtherSide({ type: CommunicationMessageType.GET_WORD_COUNT })
}, [editorToRendererCommunicator])
)
}
}, [editorToRendererCommunicator, rendererReady])
return (
<NoteInfoLine icon={'align-left'} size={'2x'}>

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -14,6 +14,7 @@ import { RendererType } from '../../render-page/window-post-message-communicator
import { useSetCheckboxInEditor } from './hooks/use-set-checkbox-in-editor'
import { useOnScrollWithLineOffset } from './hooks/use-on-scroll-with-line-offset'
import { useScrollStateWithoutLineOffset } from './hooks/use-scroll-state-without-line-offset'
import { setRendererStatus } from '../../../redux/renderer-status/methods'
export type EditorDocumentRendererProps = Omit<
RenderIframeProps,
@ -42,6 +43,7 @@ export const EditorDocumentRenderer: React.FC<EditorDocumentRendererProps> = ({
onTaskCheckedChange={setCheckboxInEditor}
rendererType={noteType === NoteType.SLIDE ? RendererType.SLIDESHOW : RendererType.DOCUMENT}
markdownContentLines={trimmedContentLines}
onRendererStatusChange={setRendererStatus}
/>
)
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -13,8 +13,9 @@ import { useSendToRenderer } from '../../../render-page/window-post-message-comm
* Sends the current dark mode setting to the renderer.
*
* @param forcedDarkMode Overwrites the value from the global application states if set.
* @param rendererReady Defines if the target renderer is ready
*/
export const useSendDarkModeStatusToRenderer = (forcedDarkMode?: boolean): void => {
export const useSendDarkModeStatusToRenderer = (forcedDarkMode: boolean | undefined, rendererReady: boolean): void => {
const savedDarkMode = useIsDarkModeActivated()
useSendToRenderer(
@ -24,6 +25,7 @@ export const useSendDarkModeStatusToRenderer = (forcedDarkMode?: boolean): void
activated: forcedDarkMode ?? savedDarkMode
}),
[forcedDarkMode, savedDarkMode]
)
),
rendererReady
)
}

View file

@ -1,5 +1,5 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
* SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
@ -12,8 +12,9 @@ import { CommunicationMessageType } from '../../../render-page/window-post-messa
* Sends the given markdown content to the renderer.
*
* @param markdownContentLines The markdown content to send.
* @param rendererReady Defines if the target renderer is ready
*/
export const useSendMarkdownToRenderer = (markdownContentLines: string[]): void => {
export const useSendMarkdownToRenderer = (markdownContentLines: string[], rendererReady: boolean): void => {
return useSendToRenderer(
useMemo(
() => ({
@ -21,6 +22,7 @@ export const useSendMarkdownToRenderer = (markdownContentLines: string[]): void
content: markdownContentLines
}),
[markdownContentLines]
)
),
rendererReady
)
}

View file

@ -4,12 +4,12 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useCallback, useRef } from 'react'
import { useEffect, useRef } from 'react'
import type { ScrollState } from '../../synced-scroll/scroll-props'
import { CommunicationMessageType } from '../../../render-page/window-post-message-communicator/rendering-message'
import { useEffectOnRendererReady } from '../../../render-page/window-post-message-communicator/hooks/use-effect-on-renderer-ready'
import equal from 'fast-deep-equal'
import { useEditorToRendererCommunicator } from '../../render-context/editor-to-renderer-communicator-context-provider'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
/**
* Sends the given {@link ScrollState scroll state} to the renderer if the content changed.
@ -19,13 +19,12 @@ import { useEditorToRendererCommunicator } from '../../render-context/editor-to-
export const useSendScrollState = (scrollState: ScrollState | undefined): void => {
const iframeCommunicator = useEditorToRendererCommunicator()
const oldScrollState = useRef<ScrollState | undefined>(undefined)
const rendererReady = useApplicationState((state) => state.rendererStatus.rendererReady)
useEffectOnRendererReady(
useCallback(() => {
if (scrollState && !equal(scrollState, oldScrollState.current)) {
oldScrollState.current = scrollState
iframeCommunicator.sendMessageToOtherSide({ type: CommunicationMessageType.SET_SCROLL_STATE, scrollState })
}
}, [iframeCommunicator, scrollState])
)
useEffect(() => {
if (rendererReady && scrollState && !equal(scrollState, oldScrollState.current)) {
oldScrollState.current = scrollState
iframeCommunicator.sendMessageToOtherSide({ type: CommunicationMessageType.SET_SCROLL_STATE, scrollState })
}
}, [iframeCommunicator, rendererReady, scrollState])
}

View file

@ -17,9 +17,7 @@ import { CommunicationMessageType } from '../../render-page/window-post-message-
import { useEditorToRendererCommunicator } from '../render-context/editor-to-renderer-communicator-context-provider'
import { useForceRenderPageUrlOnIframeLoadCallback } from './hooks/use-force-render-page-url-on-iframe-load-callback'
import { CommunicatorImageLightbox } from './communicator-image-lightbox'
import { setRendererStatus } from '../../../redux/renderer-status/methods'
import { useEditorReceiveHandler } from '../../render-page/window-post-message-communicator/hooks/use-editor-receive-handler'
import { useIsRendererReady } from '../../render-page/window-post-message-communicator/hooks/use-is-renderer-ready'
import { useSendDarkModeStatusToRenderer } from './hooks/use-send-dark-mode-status-to-renderer'
import { useSendMarkdownToRenderer } from './hooks/use-send-markdown-to-renderer'
import { useSendScrollState } from './hooks/use-send-scroll-state'
@ -33,6 +31,7 @@ export interface RenderIframeProps extends RendererProps {
rendererType: RendererType
forcedDarkMode?: boolean
frameClasses?: string
onRendererStatusChange?: undefined | ((rendererReady: boolean) => void)
}
const log = new Logger('RenderIframe')
@ -51,6 +50,7 @@ const log = new Logger('RenderIframe')
* @param frameClasses CSS classes that should be applied to the iframe
* @param rendererType The {@link RendererType type} of the renderer to use.
* @param forcedDarkMode If set, the dark mode will be set to the given value. Otherwise, the dark mode won't be changed.
* @param onRendererStatusChange Callback that is fired when the renderer in the iframe is ready
*/
export const RenderIframe: React.FC<RenderIframeProps> = ({
markdownContentLines,
@ -61,20 +61,25 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
onMakeScrollSource,
frameClasses,
rendererType,
forcedDarkMode
forcedDarkMode,
onRendererStatusChange
}) => {
const [rendererReady, setRendererReady] = useState<boolean>(false)
const frameReference = useRef<HTMLIFrameElement>(null)
const rendererBaseUrl = useBaseUrl(ORIGIN.RENDERER)
const iframeCommunicator = useEditorToRendererCommunicator()
const resetRendererReady = useCallback(() => {
log.debug('Reset render status')
setRendererStatus(false)
setRendererReady(false)
}, [])
const rendererReady = useIsRendererReady()
const onIframeLoad = useForceRenderPageUrlOnIframeLoadCallback(frameReference, resetRendererReady)
const [frameHeight, setFrameHeight] = useState<number>(0)
useEffect(() => () => setRendererStatus(false), [iframeCommunicator])
useEffect(() => {
onRendererStatusChange?.(rendererReady)
}, [onRendererStatusChange, rendererReady])
useEffect(() => () => setRendererReady(false), [iframeCommunicator])
useEffect(() => {
if (!rendererReady) {
@ -108,7 +113,9 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
useEditorReceiveHandler(
CommunicationMessageType.ON_HEIGHT_CHANGE,
useCallback((values: OnHeightChangeMessage) => setFrameHeight?.(values.height), [setFrameHeight])
useCallback((values: OnHeightChangeMessage) => {
setFrameHeight?.(values.height)
}, [])
)
useEditorReceiveHandler(
@ -135,13 +142,13 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
rendererType
}
})
setRendererStatus(true)
setRendererReady(true)
}, [iframeCommunicator, rendererBaseUrl, rendererType])
)
useEffectOnRenderTypeChange(rendererType, onIframeLoad)
useSendDarkModeStatusToRenderer(forcedDarkMode)
useSendMarkdownToRenderer(markdownContentLines)
useSendDarkModeStatusToRenderer(forcedDarkMode, rendererReady)
useSendMarkdownToRenderer(markdownContentLines, rendererReady)
useSendScrollState(scrollState)

View file

@ -1,22 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useEffect, useState } from 'react'
import { useTranslation } from 'react-i18next'
import { fetchFrontPageContent } from '../requests'
export const useIntroPageContent = (): string[] | undefined => {
const { t } = useTranslation()
const [content, setContent] = useState<string[] | undefined>(undefined)
useEffect(() => {
fetchFrontPageContent()
.then((content) => setContent(content.split('\n')))
.catch(() => setContent(undefined))
}, [t])
return content
}

View file

@ -4,43 +4,34 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useMemo } from 'react'
import { useIntroPageContent } from './hooks/use-intro-page-content'
import { useApplicationState } from '../../hooks/common/use-application-state'
import React, { useEffect, useState } from 'react'
import { WaitSpinner } from '../common/wait-spinner/wait-spinner'
import { RenderIframe } from '../editor-page/renderer-pane/render-iframe'
import { RendererType } from '../render-page/window-post-message-communicator/rendering-message'
import { useTranslation } from 'react-i18next'
import { fetchFrontPageContent } from './requests'
/**
* Fetches the content for the customizable part of the intro page and renders it.
*/
export const IntroCustomContent: React.FC = () => {
const introPageContent = useIntroPageContent()
const rendererReady = useApplicationState((state) => state.rendererStatus.rendererReady)
const { t } = useTranslation()
const [content, setContent] = useState<string[] | undefined>(undefined)
const spinner = useMemo(() => {
if (!rendererReady && introPageContent !== undefined) {
return <WaitSpinner />
}
}, [introPageContent, rendererReady])
useEffect(() => {
fetchFrontPageContent()
.then((content) => setContent(content.split('\n')))
.catch(() => setContent(undefined))
}, [t])
const introContent = useMemo(() => {
if (introPageContent !== undefined) {
return (
<RenderIframe
frameClasses={'w-100 overflow-y-hidden'}
markdownContentLines={introPageContent}
rendererType={RendererType.INTRO}
forcedDarkMode={true}
/>
)
}
}, [introPageContent])
return (
<Fragment>
{spinner}
{introContent}
</Fragment>
return content === undefined ? (
<WaitSpinner />
) : (
<RenderIframe
frameClasses={'w-100 overflow-y-hidden'}
markdownContentLines={content}
rendererType={RendererType.INTRO}
forcedDarkMode={true}
/>
)
}

View file

@ -1,23 +0,0 @@
/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useEffect } from 'react'
import { useApplicationState } from '../../../../hooks/common/use-application-state'
/**
* Executes the given callback if it changes or the renderer is ready for receiving messages.
*
* @param sendOnReadyCallback The callback that should get executed.
*/
export const useEffectOnRendererReady = (sendOnReadyCallback: () => void): void => {
const rendererReady = useApplicationState((state) => state.rendererStatus.rendererReady)
useEffect(() => {
if (rendererReady) {
sendOnReadyCallback()
}
}, [rendererReady, sendOnReadyCallback])
}

View file

@ -4,27 +4,26 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { useCallback } from 'react'
import { useEffect } from 'react'
import type { CommunicationMessages, EditorToRendererMessageType } from '../rendering-message'
import { useEditorToRendererCommunicator } from '../../../editor-page/render-context/editor-to-renderer-communicator-context-provider'
import type { MessagePayload } from '../window-post-message-communicator'
import { useEffectOnRendererReady } from './use-effect-on-renderer-ready'
/**
* Sends the given message to the renderer.
*
* @param message The message to send
* @param rendererReady Defines if the target renderer is ready
*/
export const useSendToRenderer = (
message: undefined | Extract<CommunicationMessages, MessagePayload<EditorToRendererMessageType>>
message: undefined | Extract<CommunicationMessages, MessagePayload<EditorToRendererMessageType>>,
rendererReady: boolean
): void => {
const iframeCommunicator = useEditorToRendererCommunicator()
useEffectOnRendererReady(
useCallback(() => {
if (message) {
iframeCommunicator.sendMessageToOtherSide(message)
}
}, [iframeCommunicator, message])
)
useEffect(() => {
if (message && rendererReady) {
iframeCommunicator.sendMessageToOtherSide(message)
}
}, [iframeCommunicator, message, rendererReady])
}

View file

@ -15,6 +15,7 @@ import { useTranslation } from 'react-i18next'
import { useTrimmedNoteMarkdownContentWithoutFrontmatter } from '../../hooks/common/use-trimmed-note-markdown-content-without-frontmatter'
import { useSendToRenderer } from '../render-page/window-post-message-communicator/hooks/use-send-to-renderer'
import { useApplicationState } from '../../hooks/common/use-application-state'
import { setRendererStatus } from '../../redux/renderer-status/methods'
/**
* Renders the current markdown content as a slideshow.
@ -24,6 +25,7 @@ export const SlideShowPageContent: React.FC = () => {
useTranslation()
const slideOptions = useApplicationState((state) => state.noteDetails.frontmatter.slideOptions)
const rendererReady = useApplicationState((state) => state.rendererStatus.rendererReady)
useSendToRenderer(
useMemo(
() => ({
@ -31,7 +33,8 @@ export const SlideShowPageContent: React.FC = () => {
slideOptions
}),
[slideOptions]
)
),
rendererReady
)
return (
@ -41,6 +44,7 @@ export const SlideShowPageContent: React.FC = () => {
markdownContentLines={markdownContentLines}
rendererType={RendererType.SLIDESHOW}
onFirstHeadingChange={updateNoteTitleByFirstHeading}
onRendererStatusChange={setRendererStatus}
/>
</div>
)