mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-13 22:54:42 -04:00
Fix reveal (#1563)
Fix race condition in slide show Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
3958ef550d
commit
e84ed1398f
5 changed files with 84 additions and 19 deletions
|
@ -8,7 +8,7 @@ import React, { useCallback, useEffect, useRef } from 'react'
|
||||||
|
|
||||||
export const useExtractFirstHeadline = (
|
export const useExtractFirstHeadline = (
|
||||||
documentElement: React.RefObject<HTMLDivElement>,
|
documentElement: React.RefObject<HTMLDivElement>,
|
||||||
content: string,
|
content: string | undefined,
|
||||||
onFirstHeadingChange?: (firstHeading: string | undefined) => void
|
onFirstHeadingChange?: (firstHeading: string | undefined) => void
|
||||||
): void => {
|
): void => {
|
||||||
const extractInnerText = useCallback((node: ChildNode | null): string => {
|
const extractInnerText = useCallback((node: ChildNode | null): string => {
|
||||||
|
|
|
@ -4,22 +4,39 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { useEffect, useState } from 'react'
|
import { useEffect, useRef, useState } from 'react'
|
||||||
import Reveal from 'reveal.js'
|
import Reveal from 'reveal.js'
|
||||||
import { Logger } from '../../../utils/logger'
|
import { Logger } from '../../../utils/logger'
|
||||||
import { SlideOptions } from '../../common/note-frontmatter/types'
|
import { SlideOptions } from '../../common/note-frontmatter/types'
|
||||||
|
|
||||||
const log = new Logger('reveal.js')
|
const log = new Logger('reveal.js')
|
||||||
|
|
||||||
export const useReveal = (content: string, slideOptions?: SlideOptions): void => {
|
export enum REVEAL_STATUS {
|
||||||
|
NOT_INITIALISED,
|
||||||
|
INITIALISING,
|
||||||
|
INITIALISED
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SlideState {
|
||||||
|
indexHorizontal: number
|
||||||
|
indexVertical: number
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialSlideState: SlideState = {
|
||||||
|
indexHorizontal: 0,
|
||||||
|
indexVertical: 0
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useReveal = (content: string, slideOptions?: SlideOptions): REVEAL_STATUS => {
|
||||||
const [deck, setDeck] = useState<Reveal>()
|
const [deck, setDeck] = useState<Reveal>()
|
||||||
const [isInitialized, setIsInitialized] = useState<boolean>(false)
|
const [revealStatus, setRevealStatus] = useState<REVEAL_STATUS>(REVEAL_STATUS.NOT_INITIALISED)
|
||||||
|
const currentSlideState = useRef<SlideState>(initialSlideState)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (isInitialized) {
|
if (revealStatus !== REVEAL_STATUS.NOT_INITIALISED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
setIsInitialized(true)
|
setRevealStatus(REVEAL_STATUS.INITIALISING)
|
||||||
log.debug('Initialize with slide options', slideOptions)
|
log.debug('Initialize with slide options', slideOptions)
|
||||||
const reveal = new Reveal({})
|
const reveal = new Reveal({})
|
||||||
reveal
|
reveal
|
||||||
|
@ -27,27 +44,43 @@ export const useReveal = (content: string, slideOptions?: SlideOptions): void =>
|
||||||
.then(() => {
|
.then(() => {
|
||||||
reveal.layout()
|
reveal.layout()
|
||||||
reveal.slide(0, 0, 0)
|
reveal.slide(0, 0, 0)
|
||||||
|
reveal.addEventListener('slidechanged', (event) => {
|
||||||
|
currentSlideState.current = {
|
||||||
|
indexHorizontal: event.indexh,
|
||||||
|
indexVertical: event.indexv ?? 0
|
||||||
|
} as SlideState
|
||||||
|
})
|
||||||
|
|
||||||
setDeck(reveal)
|
setDeck(reveal)
|
||||||
|
setRevealStatus(REVEAL_STATUS.INITIALISED)
|
||||||
log.debug('Initialisation finished')
|
log.debug('Initialisation finished')
|
||||||
})
|
})
|
||||||
.catch((error: Error) => {
|
.catch((error: Error) => {
|
||||||
log.error('Error while initializing reveal.js', error)
|
log.error('Error while initializing reveal.js', error)
|
||||||
})
|
})
|
||||||
}, [isInitialized, slideOptions])
|
}, [revealStatus, slideOptions])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!deck) {
|
if (!deck || revealStatus !== REVEAL_STATUS.INITIALISED) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.debug('Sync deck')
|
log.debug('Sync deck')
|
||||||
deck.layout()
|
deck.sync()
|
||||||
}, [content, deck])
|
deck.slide(currentSlideState.current.indexHorizontal, currentSlideState.current.indexVertical)
|
||||||
|
}, [content, deck, revealStatus])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!deck || slideOptions === undefined || Object.keys(slideOptions).length === 0) {
|
if (
|
||||||
|
!deck ||
|
||||||
|
slideOptions === undefined ||
|
||||||
|
Object.keys(slideOptions).length === 0 ||
|
||||||
|
revealStatus !== REVEAL_STATUS.INITIALISED
|
||||||
|
) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
log.debug('Apply config', slideOptions)
|
log.debug('Apply config', slideOptions)
|
||||||
deck.configure(slideOptions)
|
deck.configure(slideOptions)
|
||||||
}, [deck, slideOptions])
|
}, [deck, revealStatus, slideOptions])
|
||||||
|
|
||||||
|
return revealStatus
|
||||||
}
|
}
|
||||||
|
|
22
src/components/markdown-renderer/loading-slide.tsx
Normal file
22
src/components/markdown-renderer/loading-slide.tsx
Normal file
|
@ -0,0 +1,22 @@
|
||||||
|
/*
|
||||||
|
* SPDX-FileCopyrightText: 2021 The HedgeDoc developers (see AUTHORS file)
|
||||||
|
*
|
||||||
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
|
*/
|
||||||
|
|
||||||
|
import React from 'react'
|
||||||
|
import { Trans, useTranslation } from 'react-i18next'
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows a static text placeholder while reveal.js is loading.
|
||||||
|
*/
|
||||||
|
export const LoadingSlide: React.FC = () => {
|
||||||
|
useTranslation()
|
||||||
|
return (
|
||||||
|
<section>
|
||||||
|
<h1>
|
||||||
|
<Trans i18nKey={'common.loading'} />
|
||||||
|
</h1>
|
||||||
|
</section>
|
||||||
|
)
|
||||||
|
}
|
|
@ -12,7 +12,7 @@ import { useExtractFirstHeadline } from './hooks/use-extract-first-headline'
|
||||||
import { TocAst } from 'markdown-it-toc-done-right'
|
import { TocAst } from 'markdown-it-toc-done-right'
|
||||||
import { useOnRefChange } from './hooks/use-on-ref-change'
|
import { useOnRefChange } from './hooks/use-on-ref-change'
|
||||||
import { useTrimmedContent } from './hooks/use-trimmed-content'
|
import { useTrimmedContent } from './hooks/use-trimmed-content'
|
||||||
import { useReveal } from './hooks/use-reveal'
|
import { REVEAL_STATUS, useReveal } from './hooks/use-reveal'
|
||||||
import './slideshow.scss'
|
import './slideshow.scss'
|
||||||
import { ScrollProps } from '../editor-page/synced-scroll/scroll-props'
|
import { ScrollProps } from '../editor-page/synced-scroll/scroll-props'
|
||||||
import { DocumentLengthLimitReachedAlert } from './document-length-limit-reached-alert'
|
import { DocumentLengthLimitReachedAlert } from './document-length-limit-reached-alert'
|
||||||
|
@ -20,6 +20,7 @@ import { BasicMarkdownItConfigurator } from './markdown-it-configurator/basic-ma
|
||||||
import { SlideOptions } from '../common/note-frontmatter/types'
|
import { SlideOptions } from '../common/note-frontmatter/types'
|
||||||
import { processRevealCommentNodes } from './process-reveal-comment-nodes'
|
import { processRevealCommentNodes } from './process-reveal-comment-nodes'
|
||||||
import { CommonMarkdownRendererProps } from './common-markdown-renderer-props'
|
import { CommonMarkdownRendererProps } from './common-markdown-renderer-props'
|
||||||
|
import { LoadingSlide } from './loading-slide'
|
||||||
|
|
||||||
export interface SlideshowMarkdownRendererProps extends CommonMarkdownRendererProps {
|
export interface SlideshowMarkdownRendererProps extends CommonMarkdownRendererProps {
|
||||||
slideOptions: SlideOptions
|
slideOptions: SlideOptions
|
||||||
|
@ -59,17 +60,26 @@ export const SlideshowMarkdownRenderer: React.FC<SlideshowMarkdownRendererProps
|
||||||
replacers,
|
replacers,
|
||||||
processRevealCommentNodes
|
processRevealCommentNodes
|
||||||
)
|
)
|
||||||
|
const revealStatus = useReveal(content, slideOptions)
|
||||||
|
|
||||||
useExtractFirstHeadline(markdownBodyRef, content, onFirstHeadingChange)
|
useExtractFirstHeadline(
|
||||||
|
markdownBodyRef,
|
||||||
|
revealStatus === REVEAL_STATUS.INITIALISED ? content : undefined,
|
||||||
|
onFirstHeadingChange
|
||||||
|
)
|
||||||
useOnRefChange(tocAst, onTocChange)
|
useOnRefChange(tocAst, onTocChange)
|
||||||
useReveal(content, slideOptions)
|
|
||||||
|
const slideShowDOM = useMemo(
|
||||||
|
() => (revealStatus === REVEAL_STATUS.INITIALISED ? markdownReactDom : <LoadingSlide />),
|
||||||
|
[markdownReactDom, revealStatus]
|
||||||
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<DocumentLengthLimitReachedAlert show={contentExceedsLimit} />
|
<DocumentLengthLimitReachedAlert show={contentExceedsLimit} />
|
||||||
<div className={'reveal'}>
|
<div className={'reveal'}>
|
||||||
<div ref={markdownBodyRef} className={`${className ?? ''} slides`}>
|
<div ref={markdownBodyRef} className={`${className ?? ''} slides`}>
|
||||||
{markdownReactDom}
|
{slideShowDOM}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</Fragment>
|
</Fragment>
|
||||||
|
|
6
src/external-types/reveal.js/index.d.ts
vendored
6
src/external-types/reveal.js/index.d.ts
vendored
|
@ -184,9 +184,9 @@ declare module 'reveal.js' {
|
||||||
public getPlugins(): { [name: string]: Plugin }
|
public getPlugins(): { [name: string]: Plugin }
|
||||||
|
|
||||||
// States
|
// States
|
||||||
// public addEventListener(type: string, listener: (event: any) => void, useCapture?: boolean): void
|
// Added only the events we need
|
||||||
|
public addEventListener(type: 'slidechanged', listener: (event: SlideEvent) => void, useCapture?: boolean): void
|
||||||
// public removeEventListener(type: string, listener: (event: any) => void, useCapture?: boolean): void
|
public removeEventListener(type: 'slidechanged', listener: (event: SlideEvent) => void, useCapture?: boolean): void
|
||||||
|
|
||||||
// State Checks
|
// State Checks
|
||||||
public isFirstSlide(): boolean
|
public isFirstSlide(): boolean
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue