/* * SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) * * SPDX-License-Identifier: AGPL-3.0-only */ import equal from 'fast-deep-equal' import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react' import { useApplicationState } from '../../../hooks/common/use-application-state' import { useIsDarkModeActivated } from '../../../hooks/common/use-is-dark-mode-activated' import { isTestMode } from '../../../utils/test-modes' import { RendererProps } from '../../render-page/markdown-document' import { ImageDetails, RendererType } from '../../render-page/rendering-message' import { useContextOrStandaloneIframeCommunicator } from '../render-context/iframe-communicator-context-provider' import { ScrollState } from '../synced-scroll/scroll-props' import { useOnIframeLoad } from './hooks/use-on-iframe-load' import { ShowOnPropChangeImageLightbox } from './show-on-prop-change-image-lightbox' export interface RenderIframeProps extends RendererProps { onRendererReadyChange?: (rendererReady: boolean) => void rendererType: RendererType forcedDarkMode?: boolean frameClasses?: string } export const RenderIframe: React.FC = ({ markdownContent, onTaskCheckedChange, onFrontmatterChange, scrollState, onFirstHeadingChange, onScroll, onMakeScrollSource, frameClasses, onRendererReadyChange, rendererType, forcedDarkMode }) => { const savedDarkMode = useIsDarkModeActivated() const darkMode = forcedDarkMode ?? savedDarkMode const [rendererReady, setRendererReady] = useState(false) const [lightboxDetails, setLightboxDetails] = useState(undefined) const frameReference = useRef(null) const rendererOrigin = useApplicationState((state) => state.config.iframeCommunication.rendererOrigin) const renderPageUrl = `${rendererOrigin}render` const resetRendererReady = useCallback(() => setRendererReady(false), []) const iframeCommunicator = useContextOrStandaloneIframeCommunicator() const onIframeLoad = useOnIframeLoad( frameReference, iframeCommunicator, rendererOrigin, renderPageUrl, resetRendererReady ) const [frameHeight, setFrameHeight] = useState(0) useEffect(() => { onRendererReadyChange?.(rendererReady) }, [onRendererReadyChange, rendererReady]) useEffect(() => () => iframeCommunicator.unregisterEventListener(), [iframeCommunicator]) useEffect( () => iframeCommunicator.onFirstHeadingChange(onFirstHeadingChange), [iframeCommunicator, onFirstHeadingChange] ) useEffect( () => iframeCommunicator.onFrontmatterChange(onFrontmatterChange), [iframeCommunicator, onFrontmatterChange] ) useEffect(() => iframeCommunicator.onSetScrollState(onScroll), [iframeCommunicator, onScroll]) useEffect( () => iframeCommunicator.onSetScrollSourceToRenderer(onMakeScrollSource), [iframeCommunicator, onMakeScrollSource] ) useEffect( () => iframeCommunicator.onTaskCheckboxChange(onTaskCheckedChange), [iframeCommunicator, onTaskCheckedChange] ) useEffect(() => iframeCommunicator.onImageClicked(setLightboxDetails), [iframeCommunicator]) useEffect( () => iframeCommunicator.onRendererReady(() => { iframeCommunicator.sendSetBaseConfiguration({ baseUrl: window.location.toString(), rendererType }) setRendererReady(true) }), [darkMode, rendererType, iframeCommunicator, rendererReady, scrollState] ) useEffect(() => iframeCommunicator.onHeightChange(setFrameHeight), [iframeCommunicator]) useEffect(() => { if (rendererReady) { iframeCommunicator.sendSetDarkmode(darkMode) } }, [darkMode, iframeCommunicator, rendererReady]) const oldScrollState = useRef(undefined) useEffect(() => { if (rendererReady && !equal(scrollState, oldScrollState.current)) { oldScrollState.current = scrollState iframeCommunicator.sendScrollState(scrollState) } }, [iframeCommunicator, rendererReady, scrollState]) useEffect(() => { if (rendererReady) { iframeCommunicator.sendSetMarkdownContent(markdownContent) } }, [iframeCommunicator, markdownContent, rendererReady]) return (