-
Notifications
You must be signed in to change notification settings - Fork 78
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
310421d
commit ea4da9f
Showing
14 changed files
with
362 additions
and
207 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
export { useIframeMessages } from './useIframeMessages'; | ||
export { useIframeContent } from './useIframeContent'; | ||
export { useMessageHandlers } from './useMessageHandlers'; | ||
export { useIFrameBehavior } from './useIFrameBehavior'; | ||
export { useLoadBearingHook } from './useLoadBearingHook'; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
export type UseMessageHandlersTypes = { | ||
courseId: string; | ||
navigate: (path: string) => void; | ||
dispatch: (action: any) => void; | ||
setIframeOffset: (height: number) => void; | ||
handleDeleteXBlock: (usageId: string) => void; | ||
handleRefetchXBlocks: () => void; | ||
handleDuplicateXBlock: (blockType: string, usageId: string) => void; | ||
handleManageXBlockAccess: (usageId: string) => void; | ||
}; | ||
|
||
export type MessageHandlersTypes = Record<string, (payload: any) => void>; | ||
|
||
export interface UseIFrameBehaviorTypes { | ||
id: string; | ||
iframeUrl: string; | ||
onLoaded?: boolean; | ||
} | ||
|
||
export interface UseIFrameBehaviorReturnTypes { | ||
iframeHeight: number; | ||
handleIFrameLoad: () => void; | ||
showError: boolean; | ||
hasLoaded: boolean; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
33 changes: 33 additions & 0 deletions
33
src/course-unit/xblock-container-iframe/hooks/useIframeContent.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useEffect, useCallback, RefObject } from 'react'; | ||
|
||
import { messageTypes } from '../../constants'; | ||
|
||
/** | ||
* Hook for managing iframe content and providing utilities to interact with the iframe. | ||
* | ||
* @param {React.RefObject<HTMLIFrameElement>} iframeRef - A React ref for the iframe element. | ||
* @param {(ref: React.RefObject<HTMLIFrameElement>) => void} setIframeRef - | ||
* A function to associate the iframeRef with the parent context. | ||
* @param {(type: string, payload: any) => void} sendMessageToIframe - A function to send messages to the iframe. | ||
* | ||
* @returns {Object} - An object containing utility functions. | ||
* @returns {() => void} return.refreshIframeContent - | ||
* A function to refresh the iframe content by sending a specific message. | ||
*/ | ||
export const useIframeContent = ( | ||
iframeRef: RefObject<HTMLIFrameElement>, | ||
setIframeRef: (ref: RefObject<HTMLIFrameElement>) => void, | ||
sendMessageToIframe: (type: string, payload: any) => void, | ||
): { refreshIframeContent: () => void } => { | ||
useEffect(() => { | ||
setIframeRef(iframeRef); | ||
}, [setIframeRef, iframeRef]); | ||
|
||
// TODO: this artificial delay is a temporary solution | ||
// to ensure the iframe content is properly refreshed. | ||
const refreshIframeContent = useCallback(() => { | ||
setTimeout(() => sendMessageToIframe(messageTypes.refreshXBlock, null), 1000); | ||
}, [sendMessageToIframe]); | ||
|
||
return { refreshIframeContent }; | ||
}; |
20 changes: 20 additions & 0 deletions
20
src/course-unit/xblock-container-iframe/hooks/useIframeMessages.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { useEffect } from 'react'; | ||
|
||
/** | ||
* Hook for managing and handling messages received by the iframe. | ||
* | ||
* @param {Record<string, (payload: any) => void>} messageHandlers - | ||
* A mapping of message types to their corresponding handler functions. | ||
*/ | ||
export const useIframeMessages = (messageHandlers: Record<string, (payload: any) => void>) => { | ||
useEffect(() => { | ||
const handleMessage = (event: MessageEvent) => { | ||
const { type, payload } = event.data || {}; | ||
if (type in messageHandlers) { | ||
messageHandlers[type](payload); | ||
} | ||
}; | ||
window.addEventListener('message', handleMessage); | ||
return () => window.removeEventListener('message', handleMessage); | ||
}, [messageHandlers]); | ||
}; |
33 changes: 33 additions & 0 deletions
33
src/course-unit/xblock-container-iframe/hooks/useLoadBearingHook.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,33 @@ | ||
import { useLayoutEffect, useState } from 'react'; | ||
|
||
/** | ||
* We discovered an error in Firefox where - upon iframe load - React would cease to call any | ||
* useEffect hooks until the user interacts with the page again. This is particularly confusing | ||
* when navigating between sequences, as the UI partially updates leaving the user in a nebulous | ||
* state. | ||
* | ||
* We were able to solve this error by using a layout effect to update some component state, which | ||
* executes synchronously on render. Somehow this forces React to continue it's lifecycle | ||
* immediately, rather than waiting for user interaction. This layout effect could be anywhere in | ||
* the parent tree, as far as we can tell - we chose to add a conspicuously 'load bearing' (that's | ||
* a joke) one here so it wouldn't be accidentally removed elsewhere. | ||
* | ||
* If we remove this hook when one of these happens: | ||
* 1. React figures out that there's an issue here and fixes a bug. | ||
* 2. We cease to use an iframe for unit rendering. | ||
* 3. Firefox figures out that there's an issue in their iframe loading and fixes a bug. | ||
* 4. We stop supporting Firefox. | ||
* 5. An enterprising engineer decides to create a repo that reproduces the problem, submits it to | ||
* Firefox/React for review, and they kindly help us figure out what in the world is happening | ||
* so we can fix it. | ||
* | ||
* This hook depends on the unit id just to make sure it re-evaluates whenever the ID changes. If | ||
* we change whether or not the Unit component is re-mounted when the unit ID changes, this may | ||
* become important, as this hook will otherwise only evaluate the useLayoutEffect once. | ||
*/ | ||
export const useLoadBearingHook = (id: string): void => { | ||
const setValue = useState(0)[1]; | ||
useLayoutEffect(() => { | ||
setValue(currentValue => currentValue + 1); | ||
}, [id]); | ||
}; |
38 changes: 38 additions & 0 deletions
38
src/course-unit/xblock-container-iframe/hooks/useMessageHandlers.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
import { useMemo } from 'react'; | ||
|
||
import { copyToClipboard } from '../../../generic/data/thunks'; | ||
import { messageTypes } from '../../constants'; | ||
import { MessageHandlersTypes, UseMessageHandlersTypes } from './types'; | ||
|
||
/** | ||
* Hook for creating message handlers used to handle iframe messages. | ||
* | ||
* @param params - The parameters required to create message handlers. | ||
* @returns {MessageHandlersTypes} - An object mapping message types to their handler functions. | ||
*/ | ||
export const useMessageHandlers = ({ | ||
courseId, | ||
navigate, | ||
dispatch, | ||
setIframeOffset, | ||
handleDeleteXBlock, | ||
handleRefetchXBlocks, | ||
handleDuplicateXBlock, | ||
handleManageXBlockAccess, | ||
}: UseMessageHandlersTypes): MessageHandlersTypes => useMemo(() => ({ | ||
[messageTypes.copyXBlock]: ({ usageId }) => dispatch(copyToClipboard(usageId)), | ||
[messageTypes.deleteXBlock]: ({ usageId }) => handleDeleteXBlock(usageId), | ||
[messageTypes.newXBlockEditor]: ({ blockType, usageId }) => navigate(`/course/${courseId}/editor/${blockType}/${usageId}`), | ||
[messageTypes.duplicateXBlock]: ({ blockType, usageId }) => handleDuplicateXBlock(blockType, usageId), | ||
[messageTypes.manageXBlockAccess]: ({ usageId }) => handleManageXBlockAccess(usageId), | ||
[messageTypes.refreshXBlockPositions]: handleRefetchXBlocks, | ||
[messageTypes.toggleCourseXBlockDropdown]: ({ | ||
courseXBlockDropdownHeight, | ||
}: { courseXBlockDropdownHeight: number }) => setIframeOffset(courseXBlockDropdownHeight), | ||
}), [ | ||
courseId, | ||
handleDeleteXBlock, | ||
handleRefetchXBlocks, | ||
handleDuplicateXBlock, | ||
handleManageXBlockAccess, | ||
]); |
Oops, something went wrong.