Add mutation observer to first heading title extraction logic

This detects async changes of the headline (like katex does).

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2022-07-03 00:26:29 +02:00
parent eb0162bbe3
commit 96cd8fbe81
2 changed files with 29 additions and 12 deletions

View file

@ -76,8 +76,6 @@ describe('Document Title', () => {
it('katex code looks right', () => { it('katex code looks right', () => {
cy.setCodemirrorContent(`# $\\alpha$-foo`) cy.setCodemirrorContent(`# $\\alpha$-foo`)
cy.getIframeBody().find('h1').should('contain', 'α') cy.getIframeBody().find('h1').should('contain', 'α')
//TODO: Remove workaround after https://github.com/hedgedoc/react-client/issues/1816 has been fixed.
cy.get('.cm-editor .cm-content').type('{Enter}{Enter}{Enter}{Enter}{Enter}')
cy.title().should('eq', `α-foo - HedgeDoc @ ${branding.name}`) cy.title().should('eq', `α-foo - HedgeDoc @ ${branding.name}`)
}) })
}) })

View file

@ -1,11 +1,12 @@
/* /*
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file) * SPDX-FileCopyrightText: 2022 The HedgeDoc developers (see AUTHORS file)
* *
* SPDX-License-Identifier: AGPL-3.0-only * SPDX-License-Identifier: AGPL-3.0-only
*/ */
import type React from 'react' import type React from 'react'
import { useCallback, useRef } from 'react' import { useCallback, useEffect, useMemo, useRef } from 'react'
import { Optional } from '@mrdrogdrog/optional'
/** /**
* Extracts the plain text content of a {@link ChildNode node}. * Extracts the plain text content of a {@link ChildNode node}.
@ -53,17 +54,35 @@ export const useExtractFirstHeadline = (
documentElement: React.RefObject<HTMLDivElement>, documentElement: React.RefObject<HTMLDivElement>,
onFirstHeadingChange?: (firstHeading: string | undefined) => void onFirstHeadingChange?: (firstHeading: string | undefined) => void
): (() => void) => { ): (() => void) => {
const lastFirstHeading = useRef<string | undefined>() const lastFirstHeadingContent = useRef<string | undefined>()
const currentFirstHeadingElement = useRef<HTMLHeadingElement | null>(null)
return useCallback(() => { const extractHeaderText = useCallback(() => {
if (!onFirstHeadingChange || !documentElement.current) { if (!onFirstHeadingChange) {
return return
} }
const firstHeading = documentElement.current.getElementsByTagName('h1').item(0) const headingText = extractInnerText(currentFirstHeadingElement.current).trim()
const headingText = extractInnerText(firstHeading).trim() if (headingText !== lastFirstHeadingContent.current) {
if (headingText !== lastFirstHeading.current) { lastFirstHeadingContent.current = headingText
lastFirstHeading.current = headingText
onFirstHeadingChange(headingText) onFirstHeadingChange(headingText)
} }
}, [documentElement, onFirstHeadingChange]) }, [onFirstHeadingChange])
const mutationObserver = useMemo(() => new MutationObserver(() => extractHeaderText()), [extractHeaderText])
useEffect(() => () => mutationObserver.disconnect(), [mutationObserver])
return useCallback(() => {
const foundFirstHeading = Optional.ofNullable(documentElement.current)
.map((currentDocumentElement) => currentDocumentElement.getElementsByTagName('h1').item(0))
.orElse(null)
if (foundFirstHeading === currentFirstHeadingElement.current) {
return
}
mutationObserver.disconnect()
currentFirstHeadingElement.current = foundFirstHeading
if (foundFirstHeading !== null) {
mutationObserver.observe(foundFirstHeading, { subtree: true, childList: true })
}
extractHeaderText()
}, [documentElement, extractHeaderText, mutationObserver])
} }