fix(renderer): printing properly from iframe and host

React hooks may not be called inside other react hooks
due to the "rule of hooks". Furthermore, with the recent
iframe-communicator changes, the hook for printing from
outside the iframe failed - no matter whether on normal
documents or slides. This change therefore uses two
distinct hooks that represent the event listeners for
each side, instead of a shared one.

Signed-off-by: Erik Michelson <github@erik.michelson.eu>
This commit is contained in:
Erik Michelson
2026-08-04 14:49:07 +02:00
committed by Philip Molares
parent 9c00a7021a
commit b6825bc703
5 changed files with 61 additions and 16 deletions
@@ -18,7 +18,7 @@ import { PrintWarning } from './print-warning/print-warning'
import React, { useMemo, useRef } from 'react'
import { useTranslation } from 'react-i18next'
import './print.scss'
import { usePrintKeyboardShortcut } from './hooks/use-print-keyboard-shortcut'
import { usePrintIframeKeyboardShortcut } from './hooks/use-print-keyboard-shortcut'
import { NoteType } from '@hedgedoc/commons'
import { useApplicationState } from '../../hooks/common/use-application-state'
import { buildCursorLineScrollState } from './synced-scroll/cursor-line-scroll-state'
@@ -33,7 +33,7 @@ export enum ScrollSource {
*/
export const EditorPageContent: React.FC = () => {
useTranslation()
usePrintKeyboardShortcut()
usePrintIframeKeyboardShortcut()
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
const [editorScrollState, onMarkdownRendererScroll] = useScrollState(scrollSource, ScrollSource.EDITOR)
@@ -0,0 +1,40 @@
/*
* SPDX-FileCopyrightText: 2026 The HedgeDoc developers (see AUTHORS file)
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { fireEvent, renderHook } from '@testing-library/react'
import { usePrintIframeKeyboardShortcut, usePrintSelfKeyboardShortcut } from './use-print-keyboard-shortcut'
const printIframe = jest.fn()
const printSelf = jest.fn()
jest.mock('../utils/print-iframe', () => ({
usePrintIframe: () => printIframe,
usePrintSelf: () => printSelf
}))
describe('usePrintKeyboardShortcut', () => {
beforeEach(() => {
printIframe.mockClear()
printSelf.mockClear()
})
it('prints the renderer iframe when the print shortcut is pressed', () => {
renderHook(() => usePrintIframeKeyboardShortcut())
fireEvent.keyDown(window, { ctrlKey: true, key: 'p' })
expect(printIframe).toHaveBeenCalledTimes(1)
expect(printSelf).not.toHaveBeenCalled()
})
it('prints the current window when the print shortcut is pressed in the renderer iframe', () => {
renderHook(() => usePrintSelfKeyboardShortcut())
fireEvent.keyDown(window, { ctrlKey: true, key: 'p' })
expect(printSelf).toHaveBeenCalledTimes(1)
expect(printIframe).not.toHaveBeenCalled()
})
})
@@ -4,26 +4,33 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { usePrintIframe, usePrintSelf } from '../utils/print-iframe'
import { useCallback, useEffect, useMemo } from 'react'
import { useCallback, useEffect } from 'react'
/**
* Hook to listen for the print keyboard shortcut and print the content of the renderer iframe.
*/
export const usePrintKeyboardShortcut = (): void => {
const isIframe = useMemo(() => window.top !== window.self, [])
export const usePrintIframeKeyboardShortcut = (): void => {
const printIframe = usePrintIframe()
usePrintShortcut(printIframe)
}
/**
* Hook to listen for the print keyboard shortcut and print the renderer content from within the iframe.
*/
export const usePrintSelfKeyboardShortcut = (): void => {
const printSelf = usePrintSelf()
usePrintShortcut(printSelf)
}
const usePrintShortcut = (print: () => void): void => {
const handlePrint = useCallback(
(event: KeyboardEvent): void => {
if (event.key === 'p' && (event.ctrlKey || event.metaKey)) {
event.preventDefault()
if (isIframe) {
usePrintSelf()()
} else {
usePrintIframe()()
}
print()
}
},
[isIframe]
[print]
)
useEffect(() => {
@@ -18,7 +18,7 @@ import type { RevealOptions } from 'reveal.js'
import { EventEmitter2 } from 'eventemitter2'
import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
import { setPrintMode } from '../../redux/print-mode/methods'
import { usePrintKeyboardShortcut } from '../editor-page/hooks/use-print-keyboard-shortcut'
import { usePrintSelfKeyboardShortcut } from '../editor-page/hooks/use-print-keyboard-shortcut'
/**
* Wraps the markdown rendering in an iframe.
@@ -127,7 +127,7 @@ export const RenderPageContent: React.FC = () => {
)
)
usePrintKeyboardShortcut()
usePrintSelfKeyboardShortcut()
const onMakeScrollSource = useCallback(() => {
sendScrolling.current = true
+1 -3
View File
@@ -11,9 +11,7 @@ const printModeSlice = createSlice({
name: 'printMode',
initialState,
reducers: {
setPrintMode: (state, action: PayloadAction<boolean>) => {
state = action.payload
}
setPrintMode: (_state, action: PayloadAction<boolean>) => action.payload
}
})