From 96cd8fbe816379f91f11ba053946b3eb67f12db4 Mon Sep 17 00:00:00 2001 From: Tilman Vatteroth Date: Sun, 3 Jul 2022 00:26:29 +0200 Subject: [PATCH] Add mutation observer to first heading title extraction logic This detects async changes of the headline (like katex does). Signed-off-by: Tilman Vatteroth --- cypress/e2e/documentTitle.spec.ts | 2 - .../hooks/use-extract-first-headline.ts | 39 ++++++++++++++----- 2 files changed, 29 insertions(+), 12 deletions(-) diff --git a/cypress/e2e/documentTitle.spec.ts b/cypress/e2e/documentTitle.spec.ts index 6edc80931..f450237d4 100644 --- a/cypress/e2e/documentTitle.spec.ts +++ b/cypress/e2e/documentTitle.spec.ts @@ -76,8 +76,6 @@ describe('Document Title', () => { it('katex code looks right', () => { cy.setCodemirrorContent(`# $\\alpha$-foo`) 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}`) }) }) diff --git a/src/components/markdown-renderer/hooks/use-extract-first-headline.ts b/src/components/markdown-renderer/hooks/use-extract-first-headline.ts index 67ebcfa11..d8b6e9350 100644 --- a/src/components/markdown-renderer/hooks/use-extract-first-headline.ts +++ b/src/components/markdown-renderer/hooks/use-extract-first-headline.ts @@ -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 */ 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}. @@ -53,17 +54,35 @@ export const useExtractFirstHeadline = ( documentElement: React.RefObject, onFirstHeadingChange?: (firstHeading: string | undefined) => void ): (() => void) => { - const lastFirstHeading = useRef() + const lastFirstHeadingContent = useRef() + const currentFirstHeadingElement = useRef(null) - return useCallback(() => { - if (!onFirstHeadingChange || !documentElement.current) { + const extractHeaderText = useCallback(() => { + if (!onFirstHeadingChange) { return } - const firstHeading = documentElement.current.getElementsByTagName('h1').item(0) - const headingText = extractInnerText(firstHeading).trim() - if (headingText !== lastFirstHeading.current) { - lastFirstHeading.current = headingText + const headingText = extractInnerText(currentFirstHeadingElement.current).trim() + if (headingText !== lastFirstHeadingContent.current) { + lastFirstHeadingContent.current = 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]) }