Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: plugin changes for global shell #1373

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
74 changes: 62 additions & 12 deletions services/plugin/src/Plugin.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,16 @@
import { AlertsManagerContext } from '@dhis2/app-service-alerts'
import { useDataQuery } from '@dhis2/app-service-data'
import postRobot from 'post-robot'
import React, { useContext, useEffect, useMemo, useRef, useState } from 'react'
import React, {
ReactEventHandler,
SyntheticEvent,
useCallback,
useContext,
useEffect,
useMemo,
useRef,
useState,
} from 'react'
import PluginError from './PluginError'

type PluginProps = {
Expand Down Expand Up @@ -46,6 +55,8 @@ type PluginProps = {
clientWidth?: string | number
/** Props that will be sent to the plugin */
propsToPassNonMemoized?: any
/** Event callback that will be called during the iframe's Load event */
onLoad?: ReactEventHandler<HTMLIFrameElement>
}

const appsInfoQuery = {
Expand All @@ -71,6 +82,7 @@ const getPluginEntryPoint = ({
export const Plugin = ({
pluginSource,
pluginShortName,
onLoad,
height,
width,
className,
Expand Down Expand Up @@ -118,6 +130,8 @@ export const Plugin = ({
// Becomes `true` when we get the first message from a plugin, so we know
// it's ready to send messages to
const communicationReceivedRef = useRef(false)
// Tracks post-robot listeners; may change if plugin navigates to a new URL
const propsFromParentListenerRef = useRef<any>()

// If plugin source changes, reset communicationReceived so new listeners
// can be set up on the new window. This also helps avoid sending prop
Expand All @@ -139,7 +153,7 @@ export const Plugin = ({
// plugin, set up a listener for its request for initial props. If we have
// received communication from the plugin, then on any props update, we can
// send them to the plugin
useEffect(() => {
const setUpCommunication = useCallback(() => {
if (!iframeRef.current) {
return
}
Expand All @@ -160,19 +174,29 @@ export const Plugin = ({
// If iframe has not sent initial request, set up a listener
// (this will still be set up if plugin needs to reload)
if (!communicationReceivedRef.current && !inErrorState) {
const listener = postRobot.on(
'getPropsFromParent',
// listen for messages coming only from the iframe rendered by this component
{ window: iframeRef.current.contentWindow },
(): any => {
communicationReceivedRef.current = true
return iframeProps
}
)
return () => listener.cancel()
// avoid setting up twice
// todo: any race conditions here?
if (!propsFromParentListenerRef.current) {
propsFromParentListenerRef.current = postRobot.on(
'getPropsFromParent',
// listen for messages coming only from the iframe rendered by this component
{ window: iframeRef.current.contentWindow },
(): any => {
communicationReceivedRef.current = true
return iframeProps
}
)
}
// return clean-up function
return () => {
propsFromParentListenerRef.current.cancel()
propsFromParentListenerRef.current = null
}
}

// If iframe has sent initial request, send new props
// (don't do before to avoid sending messages before listeners
// are ready)
if (
communicationReceivedRef.current &&
iframeRef.current.contentWindow &&
Expand All @@ -194,6 +218,31 @@ export const Plugin = ({
clientWidth,
])

useEffect(() => {
// return the clean-up function
return setUpCommunication()
}, [setUpCommunication])

const handleLoad = useCallback(
(event: SyntheticEvent<HTMLIFrameElement, Event>) => {
// reset communication received
communicationReceivedRef.current = false
if (propsFromParentListenerRef.current) {
propsFromParentListenerRef.current.cancel()
propsFromParentListenerRef.current = null
}

// Need to set this up again whenever the iframe contentWindow
// changes (e.g. navigations or reloads)
setUpCommunication()

if (onLoad) {
onLoad(event)
}
},
[onLoad, setUpCommunication]
)

if (data && !pluginEntryPoint) {
return (
<PluginError
Expand All @@ -220,6 +269,7 @@ export const Plugin = ({
width={clientWidth ? resizedWidth : width ?? '100%'}
height={height ?? resizedHeight}
style={{ border: 'none' }}
onLoad={handleLoad}
/>
)
}
Loading