diff --git a/frontend/src/components/common/renderer-iframe/hooks/use-force-render-page-url-on-iframe-load-callback.ts b/frontend/src/components/common/renderer-iframe/hooks/use-force-render-page-url-on-iframe-load-callback.ts index 07c587eb0..339f46b29 100644 --- a/frontend/src/components/common/renderer-iframe/hooks/use-force-render-page-url-on-iframe-load-callback.ts +++ b/frontend/src/components/common/renderer-iframe/hooks/use-force-render-page-url-on-iframe-load-callback.ts @@ -7,7 +7,9 @@ import { ORIGIN, useBaseUrl } from '../../../../hooks/common/use-base-url' import { Logger } from '../../../../utils/logger' import { useEditorToRendererCommunicator } from '../../../editor-page/render-context/editor-to-renderer-communicator-context-provider' import type { RefObject } from 'react' -import { useCallback, useMemo, useRef } from 'react' +import { useCallback, useEffect, useMemo, useRef } from 'react' +import { useTimeoutFn } from '../../../../hooks/common/use-timeout-fn' +import { isTestMode } from '../../../../utils/test-modes' const log = new Logger('IframeLoader') @@ -31,7 +33,9 @@ export const useForceRenderPageUrlOnIframeLoadCallback = ( }, [iframeCommunicator, rendererBaseUrl]) const redirectionInProgress = useRef(false) - return useCallback(() => { + const loadedAtLeastOnce = useRef(false) + + const onIframeLoad = useCallback(() => { const frame = iFrameReference.current if (!frame) { @@ -45,9 +49,31 @@ export const useForceRenderPageUrlOnIframeLoadCallback = ( } else { const oldUrl = frame.src === '' ? '(none)' : frame.src log.warn(`Navigated away from unknown URL. Was ${oldUrl}. Forcing back to ${forcedUrl}`) + loadedAtLeastOnce.current = true onNavigateAway?.() redirectionInProgress.current = true frame.src = forcedUrl } }, [iFrameReference, onNavigateAway, forcedUrl]) + + const [startForceTimer, stopForceTimer] = useTimeoutFn( + 500, + useCallback(() => { + if (loadedAtLeastOnce.current) { + return + } + log.debug('Forced load of iframe') + onIframeLoad() + }, [onIframeLoad]) + ) + + useEffect(() => { + if (!isTestMode) { + return + } + startForceTimer() + return () => stopForceTimer() + }, [startForceTimer, stopForceTimer]) + + return onIframeLoad } diff --git a/frontend/src/hooks/common/use-timeout-fn.ts b/frontend/src/hooks/common/use-timeout-fn.ts new file mode 100644 index 000000000..2cb523030 --- /dev/null +++ b/frontend/src/hooks/common/use-timeout-fn.ts @@ -0,0 +1,36 @@ +/* + * SPDX-FileCopyrightText: 2023 The HedgeDoc developers (see AUTHORS file) + * + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { useCallback, useRef } from 'react' + +/** + * Creates a timer with the given timeout and callback. + * The timer is not started automatically. + * + * @param timeout The timeout in milliseconds + * @param callback The callback to execute when the time is up + * @return [startTimer, stopTimer] Functions to start and stop the timeout + */ +export const useTimeoutFn = (timeout: number, callback: () => void) => { + const timerRef = useRef(null) + + const stopTimer = useCallback(() => { + if (timerRef.current === null) { + return + } + clearTimeout(timerRef.current) + timerRef.current = null + }, []) + + const startTimer = useCallback(() => { + if (timerRef.current !== null) { + return + } + timerRef.current = setTimeout(callback, timeout) + }, [callback, timeout]) + + return [startTimer, stopTimer] +}