mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2026-08-07 07:14:49 +00:00
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:
committed by
Philip Molares
parent
9c00a7021a
commit
b6825bc703
@@ -18,7 +18,7 @@ import { PrintWarning } from './print-warning/print-warning'
|
|||||||
import React, { useMemo, useRef } from 'react'
|
import React, { useMemo, useRef } from 'react'
|
||||||
import { useTranslation } from 'react-i18next'
|
import { useTranslation } from 'react-i18next'
|
||||||
import './print.scss'
|
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 { NoteType } from '@hedgedoc/commons'
|
||||||
import { useApplicationState } from '../../hooks/common/use-application-state'
|
import { useApplicationState } from '../../hooks/common/use-application-state'
|
||||||
import { buildCursorLineScrollState } from './synced-scroll/cursor-line-scroll-state'
|
import { buildCursorLineScrollState } from './synced-scroll/cursor-line-scroll-state'
|
||||||
@@ -33,7 +33,7 @@ export enum ScrollSource {
|
|||||||
*/
|
*/
|
||||||
export const EditorPageContent: React.FC = () => {
|
export const EditorPageContent: React.FC = () => {
|
||||||
useTranslation()
|
useTranslation()
|
||||||
usePrintKeyboardShortcut()
|
usePrintIframeKeyboardShortcut()
|
||||||
|
|
||||||
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
const scrollSource = useRef<ScrollSource>(ScrollSource.EDITOR)
|
||||||
const [editorScrollState, onMarkdownRendererScroll] = useScrollState(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
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
import { usePrintIframe, usePrintSelf } from '../utils/print-iframe'
|
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.
|
* Hook to listen for the print keyboard shortcut and print the content of the renderer iframe.
|
||||||
*/
|
*/
|
||||||
export const usePrintKeyboardShortcut = (): void => {
|
export const usePrintIframeKeyboardShortcut = (): void => {
|
||||||
const isIframe = useMemo(() => window.top !== window.self, [])
|
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(
|
const handlePrint = useCallback(
|
||||||
(event: KeyboardEvent): void => {
|
(event: KeyboardEvent): void => {
|
||||||
if (event.key === 'p' && (event.ctrlKey || event.metaKey)) {
|
if (event.key === 'p' && (event.ctrlKey || event.metaKey)) {
|
||||||
event.preventDefault()
|
event.preventDefault()
|
||||||
if (isIframe) {
|
print()
|
||||||
usePrintSelf()()
|
|
||||||
} else {
|
|
||||||
usePrintIframe()()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[isIframe]
|
[print]
|
||||||
)
|
)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ import type { RevealOptions } from 'reveal.js'
|
|||||||
import { EventEmitter2 } from 'eventemitter2'
|
import { EventEmitter2 } from 'eventemitter2'
|
||||||
import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
|
import React, { useCallback, useDeferredValue, useEffect, useMemo, useRef, useState } from 'react'
|
||||||
import { setPrintMode } from '../../redux/print-mode/methods'
|
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.
|
* Wraps the markdown rendering in an iframe.
|
||||||
@@ -127,7 +127,7 @@ export const RenderPageContent: React.FC = () => {
|
|||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
usePrintKeyboardShortcut()
|
usePrintSelfKeyboardShortcut()
|
||||||
|
|
||||||
const onMakeScrollSource = useCallback(() => {
|
const onMakeScrollSource = useCallback(() => {
|
||||||
sendScrolling.current = true
|
sendScrolling.current = true
|
||||||
|
|||||||
@@ -11,9 +11,7 @@ const printModeSlice = createSlice({
|
|||||||
name: 'printMode',
|
name: 'printMode',
|
||||||
initialState,
|
initialState,
|
||||||
reducers: {
|
reducers: {
|
||||||
setPrintMode: (state, action: PayloadAction<boolean>) => {
|
setPrintMode: (_state, action: PayloadAction<boolean>) => action.payload
|
||||||
state = action.payload
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user