Skip to content

Commit

Permalink
feat: support communication across multiple windows (navigations, rel…
Browse files Browse the repository at this point in the history
…oads)
  • Loading branch information
KaiVandivier committed Feb 23, 2024
1 parent 382d0da commit 5edfbc7
Showing 1 changed file with 61 additions and 27 deletions.
88 changes: 61 additions & 27 deletions services/plugin/src/Plugin.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { useDataQuery } from '@dhis2/app-service-data'
import postRobot from 'post-robot'
import React, {
ReactEventHandler,
useCallback,
useContext,
useEffect,
useMemo,
Expand Down Expand Up @@ -65,8 +66,7 @@ export const Plugin = ({

const [communicationReceived, setCommunicationReceived] =
useState<boolean>(false)
const [prevCommunicationReceived, setPrevCommunicationReceived] =
useState<boolean>(false)
const [, setPrevCommunicationReceived] = useState<boolean>(false)

const [inErrorState, setInErrorState] = useState<boolean>(false)
const [pluginHeight, setPluginHeight] = useState<string | number>('150px')
Expand All @@ -93,18 +93,25 @@ export const Plugin = ({
/* eslint-enable react-hooks/exhaustive-deps */
)

useEffect(() => {
setCommunicationReceived(false)
}, [pluginEntryPoint])
const propsFromParentListenerRef = useRef<any>()

useEffect(() => {
const setUpCommunication = useCallback(() => {
// use function arg to set state here to avoid requiring dependency
let prevCommunication
setPrevCommunicationReceived((prevValue) => {
prevCommunication = prevValue
return communicationReceived
})
// if communicationReceived switches from false to true, the props have been sent
const prevCommunication = prevCommunicationReceived
setPrevCommunicationReceived(communicationReceived)
// the following check avoids sending the props twice in a row
if (prevCommunication === false && communicationReceived === true) {
return
}

if (inErrorState) {
return
}

if (iframeRef?.current) {
const iframeProps = {
...memoizedPropsToPass,
Expand All @@ -116,25 +123,30 @@ export const Plugin = ({
}

// if iframe has not sent initial request, set up a listener
if (!communicationReceived && !inErrorState) {
const listener = postRobot.on(
'getPropsFromParent',
// listen for messages coming only from the iframe rendered by this component
{ window: iframeRef.current.contentWindow },
(): any => {
setCommunicationReceived(true)
return iframeProps
}
)
return () => listener.cancel()
if (!communicationReceived) {
// avoid setting up twice
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 => {
setCommunicationReceived(true)
return iframeProps
}
)
}
// return clean-up function
return () => {
propsFromParentListenerRef.current.cancel()
propsFromParentListenerRef.current = null
}
}

// if iframe has sent initial request, send new props
if (
communicationReceived &&
iframeRef.current.contentWindow &&
!inErrorState
) {
// (don't do before to avoid sending messages before listeners
// are ready)
if (iframeRef.current.contentWindow) {
postRobot
.send(
iframeRef.current.contentWindow,
Expand All @@ -147,10 +159,32 @@ export const Plugin = ({
})
}
}
// prevCommunicationReceived update should not retrigger this hook
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [memoizedPropsToPass, communicationReceived, inErrorState, alertsAdd])

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

const handleLoad = useCallback(
(event) => {
setCommunicationReceived(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 @@ -176,7 +210,7 @@ export const Plugin = ({
height: '100%',
border: 'none',
}}
onLoad={onLoad}
onLoad={handleLoad}
></iframe>
</div>
)
Expand Down

0 comments on commit 5edfbc7

Please sign in to comment.