Add renderer ready state to global application state

Signed-off-by: Tilman Vatteroth <git@tilmanvatteroth.de>
This commit is contained in:
Tilman Vatteroth 2021-06-18 20:42:17 +02:00
parent 2dad1d565e
commit cfb2de8909
12 changed files with 144 additions and 52 deletions

View file

@ -4,38 +4,74 @@
* 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> {
protected otherSide?: Window
protected otherOrigin?: string
private messageTarget?: Window
private targetOrigin?: string
private communicationEnabled: boolean
constructor() {
window.addEventListener('message', this.handleEvent.bind(this))
this.communicationEnabled = false
}
public unregisterEventListener(): void {
window.removeEventListener('message', this.handleEvent.bind(this))
}
public setOtherSide(otherSide: Window, otherOrigin: string): void {
this.otherSide = otherSide
this.otherOrigin = otherOrigin
/**
* Sets the target for message sending.
* 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
this.otherOrigin = undefined
/**
* Unsets the message target. Should be used if the old target isn't available anymore.
*/
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 {
if (this.otherSide === undefined || this.otherOrigin === undefined) {
console.error("Can't send message because otherSide is null", message)
return
if (this.messageTarget === undefined || this.targetOrigin === undefined) {
throw new IframeCommunicatorSendingError(`Other side is not set.\nMessage was: ${JSON.stringify(message)}`)
}
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

View file

@ -106,6 +106,7 @@ export class IframeEditorToRendererCommunicator extends IframeCommunicator<
const renderMessage = event.data
switch (renderMessage.type) {
case RenderIframeMessageType.RENDERER_READY:
this.enableCommunication()
this.onRendererReadyHandler?.()
return false
case RenderIframeMessageType.SET_SCROLL_SOURCE_TO_RENDERER:

View file

@ -38,7 +38,7 @@ export const IframeMarkdownRenderer: React.FC = () => {
useEffect(() => iframeCommunicator.onSetDarkMode(setDarkMode), [iframeCommunicator])
useEffect(() => iframeCommunicator.onSetScrollState(setScrollState), [iframeCommunicator, scrollState])
useEffect(
() => iframeCommunicator?.onGetWordCount(countWordsInRenderedDocument),
() => iframeCommunicator.onGetWordCount(countWordsInRenderedDocument),
[iframeCommunicator, countWordsInRenderedDocument]
)

View file

@ -46,6 +46,7 @@ export class IframeRendererToEditorCommunicator extends IframeCommunicator<
}
public sendRendererReady(): void {
this.enableCommunication()
this.sendMessageToOtherSide({
type: RenderIframeMessageType.RENDERER_READY
})