mirror of
https://github.com/hedgedoc/hedgedoc.git
synced 2025-05-14 15:14:56 -04:00
Add renderer ready state to global application state
Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
parent
2dad1d565e
commit
cfb2de8909
12 changed files with 144 additions and 52 deletions
|
@ -10,6 +10,7 @@ import { ShowIf } from '../../../common/show-if/show-if'
|
||||||
import { DocumentInfoLine } from './document-info-line'
|
import { DocumentInfoLine } from './document-info-line'
|
||||||
import { UnitalicBoldText } from './unitalic-bold-text'
|
import { UnitalicBoldText } from './unitalic-bold-text'
|
||||||
import { useIFrameEditorToRendererCommunicator } from '../../render-context/iframe-editor-to-renderer-communicator-context-provider'
|
import { useIFrameEditorToRendererCommunicator } from '../../render-context/iframe-editor-to-renderer-communicator-context-provider'
|
||||||
|
import { useApplicationState } from '../../../../hooks/common/use-application-state'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a new info line for the document information dialog that holds the
|
* Creates a new info line for the document information dialog that holds the
|
||||||
|
@ -19,17 +20,23 @@ export const DocumentInfoLineWordCount: React.FC = () => {
|
||||||
useTranslation()
|
useTranslation()
|
||||||
const iframeEditorToRendererCommunicator = useIFrameEditorToRendererCommunicator()
|
const iframeEditorToRendererCommunicator = useIFrameEditorToRendererCommunicator()
|
||||||
const [wordCount, setWordCount] = useState<number | null>(null)
|
const [wordCount, setWordCount] = useState<number | null>(null)
|
||||||
|
const rendererReady = useApplicationState((state) => state.editorConfig.rendererReady)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
iframeEditorToRendererCommunicator?.onWordCountCalculated((words) => {
|
iframeEditorToRendererCommunicator.onWordCountCalculated((words) => {
|
||||||
setWordCount(words)
|
setWordCount(words)
|
||||||
})
|
})
|
||||||
iframeEditorToRendererCommunicator?.sendGetWordCount()
|
|
||||||
return () => {
|
return () => {
|
||||||
iframeEditorToRendererCommunicator?.onWordCountCalculated(undefined)
|
iframeEditorToRendererCommunicator.onWordCountCalculated(undefined)
|
||||||
}
|
}
|
||||||
}, [iframeEditorToRendererCommunicator, setWordCount])
|
}, [iframeEditorToRendererCommunicator, setWordCount])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (rendererReady) {
|
||||||
|
iframeEditorToRendererCommunicator.sendGetWordCount()
|
||||||
|
}
|
||||||
|
}, [iframeEditorToRendererCommunicator, rendererReady])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DocumentInfoLine icon={'align-left'} size={'2x'}>
|
<DocumentInfoLine icon={'align-left'} size={'2x'}>
|
||||||
<ShowIf condition={wordCount === null}>
|
<ShowIf condition={wordCount === null}>
|
||||||
|
|
|
@ -31,7 +31,7 @@ export const IframeRendererToEditorCommunicatorContextProvider: React.FC = ({ ch
|
||||||
const editorOrigin = useSelector((state: ApplicationState) => state.config.iframeCommunication.editorOrigin)
|
const editorOrigin = useSelector((state: ApplicationState) => state.config.iframeCommunication.editorOrigin)
|
||||||
const currentIFrameCommunicator = useMemo<IframeRendererToEditorCommunicator>(() => {
|
const currentIFrameCommunicator = useMemo<IframeRendererToEditorCommunicator>(() => {
|
||||||
const newCommunicator = new IframeRendererToEditorCommunicator()
|
const newCommunicator = new IframeRendererToEditorCommunicator()
|
||||||
newCommunicator.setOtherSide(window.parent, editorOrigin)
|
newCommunicator.setMessageTarget(window.parent, editorOrigin)
|
||||||
return newCommunicator
|
return newCommunicator
|
||||||
}, [editorOrigin])
|
}, [editorOrigin])
|
||||||
|
|
||||||
|
|
|
@ -19,12 +19,12 @@ export const useOnIframeLoad = (
|
||||||
return useCallback(() => {
|
return useCallback(() => {
|
||||||
const frame = frameReference.current
|
const frame = frameReference.current
|
||||||
if (!frame || !frame.contentWindow) {
|
if (!frame || !frame.contentWindow) {
|
||||||
iframeCommunicator.unsetOtherSide()
|
iframeCommunicator.unsetMessageTarget()
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if (sendToRenderPage.current) {
|
if (sendToRenderPage.current) {
|
||||||
iframeCommunicator.setOtherSide(frame.contentWindow, rendererOrigin)
|
iframeCommunicator.setMessageTarget(frame.contentWindow, rendererOrigin)
|
||||||
sendToRenderPage.current = false
|
sendToRenderPage.current = false
|
||||||
return
|
return
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -7,6 +7,7 @@ import equal from 'fast-deep-equal'
|
||||||
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
import React, { Fragment, useCallback, useEffect, useRef, useState } from 'react'
|
||||||
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
import { useApplicationState } from '../../../hooks/common/use-application-state'
|
||||||
import { useIsDarkModeActivated } from '../../../hooks/common/use-is-dark-mode-activated'
|
import { useIsDarkModeActivated } from '../../../hooks/common/use-is-dark-mode-activated'
|
||||||
|
import { setRendererReady } from '../../../redux/editor/methods'
|
||||||
import { isTestMode } from '../../../utils/test-modes'
|
import { isTestMode } from '../../../utils/test-modes'
|
||||||
import { RendererProps } from '../../render-page/markdown-document'
|
import { RendererProps } from '../../render-page/markdown-document'
|
||||||
import { ImageDetails, RendererType } from '../../render-page/rendering-message'
|
import { ImageDetails, RendererType } from '../../render-page/rendering-message'
|
||||||
|
@ -16,7 +17,6 @@ import { useOnIframeLoad } from './hooks/use-on-iframe-load'
|
||||||
import { ShowOnPropChangeImageLightbox } from './show-on-prop-change-image-lightbox'
|
import { ShowOnPropChangeImageLightbox } from './show-on-prop-change-image-lightbox'
|
||||||
|
|
||||||
export interface RenderIframeProps extends RendererProps {
|
export interface RenderIframeProps extends RendererProps {
|
||||||
onRendererReadyChange?: (rendererReady: boolean) => void
|
|
||||||
rendererType: RendererType
|
rendererType: RendererType
|
||||||
forcedDarkMode?: boolean
|
forcedDarkMode?: boolean
|
||||||
frameClasses?: string
|
frameClasses?: string
|
||||||
|
@ -31,13 +31,11 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
|
||||||
onScroll,
|
onScroll,
|
||||||
onMakeScrollSource,
|
onMakeScrollSource,
|
||||||
frameClasses,
|
frameClasses,
|
||||||
onRendererReadyChange,
|
|
||||||
rendererType,
|
rendererType,
|
||||||
forcedDarkMode
|
forcedDarkMode
|
||||||
}) => {
|
}) => {
|
||||||
const savedDarkMode = useIsDarkModeActivated()
|
const savedDarkMode = useIsDarkModeActivated()
|
||||||
const darkMode = forcedDarkMode ?? savedDarkMode
|
const darkMode = forcedDarkMode ?? savedDarkMode
|
||||||
const [rendererReady, setRendererReady] = useState<boolean>(false)
|
|
||||||
const [lightboxDetails, setLightboxDetails] = useState<ImageDetails | undefined>(undefined)
|
const [lightboxDetails, setLightboxDetails] = useState<ImageDetails | undefined>(undefined)
|
||||||
|
|
||||||
const frameReference = useRef<HTMLIFrameElement>(null)
|
const frameReference = useRef<HTMLIFrameElement>(null)
|
||||||
|
@ -54,29 +52,51 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
|
||||||
)
|
)
|
||||||
const [frameHeight, setFrameHeight] = useState<number>(0)
|
const [frameHeight, setFrameHeight] = useState<number>(0)
|
||||||
|
|
||||||
useEffect(() => {
|
const rendererReady = useApplicationState((state) => state.editorConfig.rendererReady)
|
||||||
onRendererReadyChange?.(rendererReady)
|
|
||||||
}, [onRendererReadyChange, rendererReady])
|
|
||||||
|
|
||||||
useEffect(() => () => iframeCommunicator.unregisterEventListener(), [iframeCommunicator])
|
|
||||||
useEffect(
|
useEffect(
|
||||||
() => iframeCommunicator.onFirstHeadingChange(onFirstHeadingChange),
|
() => () => {
|
||||||
[iframeCommunicator, onFirstHeadingChange]
|
iframeCommunicator.unregisterEventListener()
|
||||||
|
setRendererReady(false)
|
||||||
|
},
|
||||||
|
[iframeCommunicator]
|
||||||
)
|
)
|
||||||
useEffect(
|
|
||||||
() => iframeCommunicator.onFrontmatterChange(onFrontmatterChange),
|
useEffect(() => {
|
||||||
[iframeCommunicator, onFrontmatterChange]
|
iframeCommunicator.onFirstHeadingChange(onFirstHeadingChange)
|
||||||
)
|
return () => iframeCommunicator.onFirstHeadingChange(undefined)
|
||||||
useEffect(() => iframeCommunicator.onSetScrollState(onScroll), [iframeCommunicator, onScroll])
|
}, [iframeCommunicator, onFirstHeadingChange])
|
||||||
useEffect(
|
|
||||||
() => iframeCommunicator.onSetScrollSourceToRenderer(onMakeScrollSource),
|
useEffect(() => {
|
||||||
[iframeCommunicator, onMakeScrollSource]
|
iframeCommunicator.onFrontmatterChange(onFrontmatterChange)
|
||||||
)
|
return () => iframeCommunicator.onFrontmatterChange(undefined)
|
||||||
useEffect(
|
}, [iframeCommunicator, onFrontmatterChange])
|
||||||
() => iframeCommunicator.onTaskCheckboxChange(onTaskCheckedChange),
|
|
||||||
[iframeCommunicator, onTaskCheckedChange]
|
useEffect(() => {
|
||||||
)
|
iframeCommunicator.onSetScrollState(onScroll)
|
||||||
useEffect(() => iframeCommunicator.onImageClicked(setLightboxDetails), [iframeCommunicator])
|
return () => iframeCommunicator.onSetScrollState(undefined)
|
||||||
|
}, [iframeCommunicator, onScroll])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
iframeCommunicator.onSetScrollSourceToRenderer(onMakeScrollSource)
|
||||||
|
return () => iframeCommunicator.onSetScrollSourceToRenderer(undefined)
|
||||||
|
}, [iframeCommunicator, onMakeScrollSource])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
iframeCommunicator.onTaskCheckboxChange(onTaskCheckedChange)
|
||||||
|
return () => iframeCommunicator.onTaskCheckboxChange(undefined)
|
||||||
|
}, [iframeCommunicator, onTaskCheckedChange])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
iframeCommunicator.onImageClicked(setLightboxDetails)
|
||||||
|
return () => iframeCommunicator.onImageClicked(undefined)
|
||||||
|
}, [iframeCommunicator])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
iframeCommunicator.onHeightChange(setFrameHeight)
|
||||||
|
return () => iframeCommunicator.onHeightChange(undefined)
|
||||||
|
}, [iframeCommunicator])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
iframeCommunicator.onRendererReady(() => {
|
iframeCommunicator.onRendererReady(() => {
|
||||||
iframeCommunicator.sendSetBaseConfiguration({
|
iframeCommunicator.sendSetBaseConfiguration({
|
||||||
|
@ -85,8 +105,8 @@ export const RenderIframe: React.FC<RenderIframeProps> = ({
|
||||||
})
|
})
|
||||||
setRendererReady(true)
|
setRendererReady(true)
|
||||||
})
|
})
|
||||||
}, [darkMode, rendererType, iframeCommunicator, rendererReady, scrollState])
|
return () => iframeCommunicator.onRendererReady(undefined)
|
||||||
useEffect(() => iframeCommunicator.onHeightChange(setFrameHeight), [iframeCommunicator])
|
}, [iframeCommunicator, rendererType])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (rendererReady) {
|
if (rendererReady) {
|
||||||
|
|
|
@ -4,7 +4,7 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, { useState } from 'react'
|
import React from 'react'
|
||||||
import { Trans } from 'react-i18next'
|
import { Trans } from 'react-i18next'
|
||||||
import { Branding } from '../common/branding/branding'
|
import { Branding } from '../common/branding/branding'
|
||||||
import {
|
import {
|
||||||
|
@ -20,10 +20,11 @@ import { ShowIf } from '../common/show-if/show-if'
|
||||||
import { RendererType } from '../render-page/rendering-message'
|
import { RendererType } from '../render-page/rendering-message'
|
||||||
import { WaitSpinner } from '../common/wait-spinner/wait-spinner'
|
import { WaitSpinner } from '../common/wait-spinner/wait-spinner'
|
||||||
import { IframeEditorToRendererCommunicatorContextProvider } from '../editor-page/render-context/iframe-editor-to-renderer-communicator-context-provider'
|
import { IframeEditorToRendererCommunicatorContextProvider } from '../editor-page/render-context/iframe-editor-to-renderer-communicator-context-provider'
|
||||||
|
import { useApplicationState } from '../../hooks/common/use-application-state'
|
||||||
|
|
||||||
export const IntroPage: React.FC = () => {
|
export const IntroPage: React.FC = () => {
|
||||||
const introPageContent = useIntroPageContent()
|
const introPageContent = useIntroPageContent()
|
||||||
const [rendererReady, setRendererReady] = useState<boolean>(true)
|
const rendererReady = useApplicationState((state) => state.editorConfig.rendererReady)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<IframeEditorToRendererCommunicatorContextProvider>
|
<IframeEditorToRendererCommunicatorContextProvider>
|
||||||
|
@ -45,7 +46,6 @@ export const IntroPage: React.FC = () => {
|
||||||
<RenderIframe
|
<RenderIframe
|
||||||
frameClasses={'w-100 overflow-y-hidden'}
|
frameClasses={'w-100 overflow-y-hidden'}
|
||||||
markdownContent={introPageContent as string}
|
markdownContent={introPageContent as string}
|
||||||
onRendererReadyChange={setRendererReady}
|
|
||||||
rendererType={RendererType.INTRO}
|
rendererType={RendererType.INTRO}
|
||||||
forcedDarkMode={true}
|
forcedDarkMode={true}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -4,38 +4,74 @@
|
||||||
* SPDX-License-Identifier: AGPL-3.0-only
|
* SPDX-License-Identifier: AGPL-3.0-only
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Error that will be thrown if a message couldn't be sent.
|
||||||
|
*/
|
||||||
|
export class IframeCommunicatorSendingError extends Error {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Base class for communication between renderer and editor.
|
||||||
|
*/
|
||||||
export abstract class IframeCommunicator<SEND, RECEIVE> {
|
export abstract class IframeCommunicator<SEND, RECEIVE> {
|
||||||
protected otherSide?: Window
|
private messageTarget?: Window
|
||||||
protected otherOrigin?: string
|
private targetOrigin?: string
|
||||||
|
private communicationEnabled: boolean
|
||||||
|
|
||||||
constructor() {
|
constructor() {
|
||||||
window.addEventListener('message', this.handleEvent.bind(this))
|
window.addEventListener('message', this.handleEvent.bind(this))
|
||||||
|
this.communicationEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public unregisterEventListener(): void {
|
public unregisterEventListener(): void {
|
||||||
window.removeEventListener('message', this.handleEvent.bind(this))
|
window.removeEventListener('message', this.handleEvent.bind(this))
|
||||||
}
|
}
|
||||||
|
|
||||||
public setOtherSide(otherSide: Window, otherOrigin: string): void {
|
/**
|
||||||
this.otherSide = otherSide
|
* Sets the target for message sending.
|
||||||
this.otherOrigin = otherOrigin
|
* Messages can be sent as soon as the communication is enabled.
|
||||||
|
*
|
||||||
|
* @see enableCommunication
|
||||||
|
* @param otherSide The target {@link Window} that should receive the messages.
|
||||||
|
* @param otherOrigin The origin from the URL of the target. If this isn't correct then the message sending will produce CORS errors.
|
||||||
|
*/
|
||||||
|
public setMessageTarget(otherSide: Window, otherOrigin: string): void {
|
||||||
|
this.messageTarget = otherSide
|
||||||
|
this.targetOrigin = otherOrigin
|
||||||
|
this.communicationEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public unsetOtherSide(): void {
|
/**
|
||||||
this.otherSide = undefined
|
* Unsets the message target. Should be used if the old target isn't available anymore.
|
||||||
this.otherOrigin = undefined
|
*/
|
||||||
|
public unsetMessageTarget(): void {
|
||||||
|
this.messageTarget = undefined
|
||||||
|
this.targetOrigin = undefined
|
||||||
|
this.communicationEnabled = false
|
||||||
}
|
}
|
||||||
|
|
||||||
public getOtherSide(): Window | undefined {
|
/**
|
||||||
return this.otherSide
|
* Enables the message communication.
|
||||||
|
* Should be called as soon as the other sides is ready to receive messages.
|
||||||
|
*/
|
||||||
|
protected enableCommunication(): void {
|
||||||
|
this.communicationEnabled = true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sends a message to the message target.
|
||||||
|
*
|
||||||
|
* @param message The message to send.
|
||||||
|
*/
|
||||||
protected sendMessageToOtherSide(message: SEND): void {
|
protected sendMessageToOtherSide(message: SEND): void {
|
||||||
if (this.otherSide === undefined || this.otherOrigin === undefined) {
|
if (this.messageTarget === undefined || this.targetOrigin === undefined) {
|
||||||
console.error("Can't send message because otherSide is null", message)
|
throw new IframeCommunicatorSendingError(`Other side is not set.\nMessage was: ${JSON.stringify(message)}`)
|
||||||
return
|
|
||||||
}
|
}
|
||||||
this.otherSide.postMessage(message, this.otherOrigin)
|
if (!this.communicationEnabled) {
|
||||||
|
throw new IframeCommunicatorSendingError(
|
||||||
|
`Communication isn't enabled. Maybe the other side is not ready?\nMessage was: ${JSON.stringify(message)}`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
this.messageTarget.postMessage(message, this.targetOrigin)
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract handleEvent(event: MessageEvent<RECEIVE>): void
|
protected abstract handleEvent(event: MessageEvent<RECEIVE>): void
|
||||||
|
|
|
@ -106,6 +106,7 @@ export class IframeEditorToRendererCommunicator extends IframeCommunicator<
|
||||||
const renderMessage = event.data
|
const renderMessage = event.data
|
||||||
switch (renderMessage.type) {
|
switch (renderMessage.type) {
|
||||||
case RenderIframeMessageType.RENDERER_READY:
|
case RenderIframeMessageType.RENDERER_READY:
|
||||||
|
this.enableCommunication()
|
||||||
this.onRendererReadyHandler?.()
|
this.onRendererReadyHandler?.()
|
||||||
return false
|
return false
|
||||||
case RenderIframeMessageType.SET_SCROLL_SOURCE_TO_RENDERER:
|
case RenderIframeMessageType.SET_SCROLL_SOURCE_TO_RENDERER:
|
||||||
|
|
|
@ -38,7 +38,7 @@ export const IframeMarkdownRenderer: React.FC = () => {
|
||||||
useEffect(() => iframeCommunicator.onSetDarkMode(setDarkMode), [iframeCommunicator])
|
useEffect(() => iframeCommunicator.onSetDarkMode(setDarkMode), [iframeCommunicator])
|
||||||
useEffect(() => iframeCommunicator.onSetScrollState(setScrollState), [iframeCommunicator, scrollState])
|
useEffect(() => iframeCommunicator.onSetScrollState(setScrollState), [iframeCommunicator, scrollState])
|
||||||
useEffect(
|
useEffect(
|
||||||
() => iframeCommunicator?.onGetWordCount(countWordsInRenderedDocument),
|
() => iframeCommunicator.onGetWordCount(countWordsInRenderedDocument),
|
||||||
[iframeCommunicator, countWordsInRenderedDocument]
|
[iframeCommunicator, countWordsInRenderedDocument]
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -46,6 +46,7 @@ export class IframeRendererToEditorCommunicator extends IframeCommunicator<
|
||||||
}
|
}
|
||||||
|
|
||||||
public sendRendererReady(): void {
|
public sendRendererReady(): void {
|
||||||
|
this.enableCommunication()
|
||||||
this.sendMessageToOtherSide({
|
this.sendMessageToOtherSide({
|
||||||
type: RenderIframeMessageType.RENDERER_READY
|
type: RenderIframeMessageType.RENDERER_READY
|
||||||
})
|
})
|
||||||
|
|
|
@ -14,7 +14,8 @@ import {
|
||||||
SetEditorLigaturesAction,
|
SetEditorLigaturesAction,
|
||||||
SetEditorPreferencesAction,
|
SetEditorPreferencesAction,
|
||||||
SetEditorSmartPasteAction,
|
SetEditorSmartPasteAction,
|
||||||
SetEditorSyncScrollAction
|
SetEditorSyncScrollAction,
|
||||||
|
SetRendererReadyAction
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
export const loadFromLocalStorage = (): EditorConfig | undefined => {
|
export const loadFromLocalStorage = (): EditorConfig | undefined => {
|
||||||
|
@ -46,6 +47,19 @@ export const setEditorMode = (editorMode: EditorMode): void => {
|
||||||
store.dispatch(action)
|
store.dispatch(action)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Dispatches a global application state change for the "renderer ready" state.
|
||||||
|
*
|
||||||
|
* @param rendererReady The new renderer ready state.
|
||||||
|
*/
|
||||||
|
export const setRendererReady = (rendererReady: boolean): void => {
|
||||||
|
const action: SetRendererReadyAction = {
|
||||||
|
type: EditorConfigActionType.SET_RENDERER_READY,
|
||||||
|
rendererReady
|
||||||
|
}
|
||||||
|
store.dispatch(action)
|
||||||
|
}
|
||||||
|
|
||||||
export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
export const setEditorSyncScroll = (syncScroll: boolean): void => {
|
||||||
const action: SetEditorSyncScrollAction = {
|
const action: SetEditorSyncScrollAction = {
|
||||||
type: EditorConfigActionType.SET_SYNC_SCROLL,
|
type: EditorConfigActionType.SET_SYNC_SCROLL,
|
||||||
|
|
|
@ -15,7 +15,8 @@ import {
|
||||||
SetEditorLigaturesAction,
|
SetEditorLigaturesAction,
|
||||||
SetEditorPreferencesAction,
|
SetEditorPreferencesAction,
|
||||||
SetEditorSmartPasteAction,
|
SetEditorSmartPasteAction,
|
||||||
SetEditorSyncScrollAction
|
SetEditorSyncScrollAction,
|
||||||
|
SetRendererReadyAction
|
||||||
} from './types'
|
} from './types'
|
||||||
|
|
||||||
const initialState: EditorConfig = {
|
const initialState: EditorConfig = {
|
||||||
|
@ -23,6 +24,7 @@ const initialState: EditorConfig = {
|
||||||
ligatures: true,
|
ligatures: true,
|
||||||
syncScroll: true,
|
syncScroll: true,
|
||||||
smartPaste: true,
|
smartPaste: true,
|
||||||
|
rendererReady: false,
|
||||||
preferences: {
|
preferences: {
|
||||||
theme: 'one-dark',
|
theme: 'one-dark',
|
||||||
keyMap: 'sublime',
|
keyMap: 'sublime',
|
||||||
|
@ -55,6 +57,11 @@ export const EditorConfigReducer: Reducer<EditorConfig, EditorConfigActions> = (
|
||||||
}
|
}
|
||||||
saveToLocalStorage(newState)
|
saveToLocalStorage(newState)
|
||||||
return newState
|
return newState
|
||||||
|
case EditorConfigActionType.SET_RENDERER_READY:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
rendererReady: (action as SetRendererReadyAction).rendererReady
|
||||||
|
}
|
||||||
case EditorConfigActionType.SET_LIGATURES:
|
case EditorConfigActionType.SET_LIGATURES:
|
||||||
newState = {
|
newState = {
|
||||||
...state,
|
...state,
|
||||||
|
|
|
@ -13,7 +13,8 @@ export enum EditorConfigActionType {
|
||||||
SET_SYNC_SCROLL = 'editor/syncScroll/set',
|
SET_SYNC_SCROLL = 'editor/syncScroll/set',
|
||||||
MERGE_EDITOR_PREFERENCES = 'editor/preferences/merge',
|
MERGE_EDITOR_PREFERENCES = 'editor/preferences/merge',
|
||||||
SET_LIGATURES = 'editor/preferences/setLigatures',
|
SET_LIGATURES = 'editor/preferences/setLigatures',
|
||||||
SET_SMART_PASTE = 'editor/preferences/setSmartPaste'
|
SET_SMART_PASTE = 'editor/preferences/setSmartPaste',
|
||||||
|
SET_RENDERER_READY = 'editor/rendererReady/set'
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface EditorConfig {
|
export interface EditorConfig {
|
||||||
|
@ -21,6 +22,7 @@ export interface EditorConfig {
|
||||||
syncScroll: boolean
|
syncScroll: boolean
|
||||||
ligatures: boolean
|
ligatures: boolean
|
||||||
smartPaste: boolean
|
smartPaste: boolean
|
||||||
|
rendererReady: boolean
|
||||||
preferences: EditorConfiguration
|
preferences: EditorConfiguration
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -28,6 +30,10 @@ export interface EditorConfigActions extends Action<EditorConfigActionType> {
|
||||||
type: EditorConfigActionType
|
type: EditorConfigActionType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface SetRendererReadyAction extends EditorConfigActions {
|
||||||
|
rendererReady: boolean
|
||||||
|
}
|
||||||
|
|
||||||
export interface SetEditorSyncScrollAction extends EditorConfigActions {
|
export interface SetEditorSyncScrollAction extends EditorConfigActions {
|
||||||
syncScroll: boolean
|
syncScroll: boolean
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue