hedgedoc/src/components/editor-page/renderer-pane/render-iframe.tsx
Tilman Vatteroth 6a6f6105b9
Upgrade to CodeMirror 6 (#1787)
Upgrade to CodeMirror 6

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
2022-02-13 11:14:01 +00:00

156 lines
5.7 KiB
TypeScript

/*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
import { isTestMode } from '../../../utils/test-modes'
import type { RendererProps } from '../../render-page/markdown-document'
import type {
OnFirstHeadingChangeMessage,
OnHeightChangeMessage,
OnTaskCheckboxChangeMessage,
RendererType,
SetScrollStateMessage
} from '../../render-page/window-post-message-communicator/rendering-message'
import { CommunicationMessageType } from '../../render-page/window-post-message-communicator/rendering-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'
import { Logger } from '../../../utils/logger'
import { useEffectOnRenderTypeChange } from './hooks/use-effect-on-render-type-change'
import { cypressAttribute, cypressId } from '../../../utils/cypress-attribute'
import { ORIGIN_TYPE, useOriginFromConfig } from '../render-context/use-origin-from-config'
export interface RenderIframeProps extends RendererProps {
rendererType: RendererType
forcedDarkMode?: boolean
frameClasses?: string
}
const log = new Logger('RenderIframe')
export const RenderIframe: React.FC<RenderIframeProps> = ({
markdownContentLines,
onTaskCheckedChange,
scrollState,
onFirstHeadingChange,
onScroll,
onMakeScrollSource,
frameClasses,
rendererType,
forcedDarkMode
}) => {
const frameReference = useRef<HTMLIFrameElement>(null)
const rendererOrigin = useOriginFromConfig(ORIGIN_TYPE.RENDERER)
const iframeCommunicator = useEditorToRendererCommunicator()
const resetRendererReady = useCallback(() => {
log.debug('Reset render status')
setRendererStatus(false)
}, [])
const rendererReady = useIsRendererReady()
const onIframeLoad = useForceRenderPageUrlOnIframeLoadCallback(frameReference, rendererOrigin, resetRendererReady)
const [frameHeight, setFrameHeight] = useState<number>(0)
useEffect(
() => () => {
iframeCommunicator.unregisterEventListener()
setRendererStatus(false)
},
[iframeCommunicator]
)
useEffect(() => {
if (!rendererReady) {
iframeCommunicator.unsetMessageTarget()
}
}, [iframeCommunicator, rendererReady])
useEditorReceiveHandler(
CommunicationMessageType.ON_FIRST_HEADING_CHANGE,
useCallback(
(values: OnFirstHeadingChangeMessage) => onFirstHeadingChange?.(values.firstHeading),
[onFirstHeadingChange]
)
)
useEditorReceiveHandler(
CommunicationMessageType.SET_SCROLL_STATE,
useCallback((values: SetScrollStateMessage) => onScroll?.(values.scrollState), [onScroll])
)
useEditorReceiveHandler(
CommunicationMessageType.ENABLE_RENDERER_SCROLL_SOURCE,
useCallback(() => onMakeScrollSource?.(), [onMakeScrollSource])
)
useEditorReceiveHandler(
CommunicationMessageType.ON_TASK_CHECKBOX_CHANGE,
useCallback(
(values: OnTaskCheckboxChangeMessage) => onTaskCheckedChange?.(values.lineInMarkdown, values.checked),
[onTaskCheckedChange]
)
)
useEditorReceiveHandler(
CommunicationMessageType.ON_HEIGHT_CHANGE,
useCallback((values: OnHeightChangeMessage) => setFrameHeight?.(values.height), [setFrameHeight])
)
useEditorReceiveHandler(
CommunicationMessageType.RENDERER_READY,
useCallback(() => {
const frame = frameReference.current
if (!frame) {
log.error('Load triggered without frame in ref')
return
}
const otherWindow = frame.contentWindow
if (!otherWindow) {
log.error('Load triggered without content window')
return
}
log.debug(`Set iframecommunicator window with origin ${rendererOrigin}`)
iframeCommunicator.setMessageTarget(otherWindow, rendererOrigin)
iframeCommunicator.enableCommunication()
iframeCommunicator.sendMessageToOtherSide({
type: CommunicationMessageType.SET_BASE_CONFIGURATION,
baseConfiguration: {
baseUrl: window.location.toString(),
rendererType
}
})
setRendererStatus(true)
}, [iframeCommunicator, rendererOrigin, rendererType])
)
useEffectOnRenderTypeChange(rendererType, onIframeLoad)
useSendScrollState(scrollState)
useSendDarkModeStatusToRenderer(forcedDarkMode)
useSendMarkdownToRenderer(markdownContentLines)
return (
<Fragment>
<CommunicatorImageLightbox />
<iframe
style={{ height: `${frameHeight}px` }}
{...cypressId('documentIframe')}
onLoad={onIframeLoad}
title='render'
{...(isTestMode() ? {} : { sandbox: 'allow-downloads allow-same-origin allow-scripts allow-popups' })}
allowFullScreen={true}
ref={frameReference}
referrerPolicy={'no-referrer'}
className={`border-0 ${frameClasses ?? ''}`}
{...cypressAttribute('renderer-ready', rendererReady ? 'true' : 'false')}
{...cypressAttribute('renderer-type', rendererType)}
/>
</Fragment>
)
}