diff --git a/package-lock.json b/package-lock.json index 6f0a4d6263..6bfd0b3a6e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -26,6 +26,7 @@ "core-js": "3.22.2", "history": "5.3.0", "js-cookie": "3.0.1", + "lodash": "^4.17.21", "lodash.camelcase": "4.3.0", "prop-types": "15.8.1", "query-string": "7.1.3", @@ -20165,8 +20166,7 @@ "node_modules/lodash": { "version": "4.17.21", "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" }, "node_modules/lodash.assignin": { "version": "4.2.0", diff --git a/package.json b/package.json index 42206be047..b50a1987a9 100644 --- a/package.json +++ b/package.json @@ -46,6 +46,7 @@ "core-js": "3.22.2", "history": "5.3.0", "js-cookie": "3.0.1", + "lodash": "^4.17.21", "lodash.camelcase": "4.3.0", "prop-types": "15.8.1", "query-string": "7.1.3", diff --git a/src/courseware/course/sequence/Unit.jsx b/src/courseware/course/sequence/Unit.jsx index 274bc68143..f7dc8bfd09 100644 --- a/src/courseware/course/sequence/Unit.jsx +++ b/src/courseware/course/sequence/Unit.jsx @@ -7,6 +7,7 @@ import React, { Suspense, useCallback, useContext, useEffect, useLayoutEffect, useState, } from 'react'; import { useDispatch } from 'react-redux'; +import { throttle } from 'lodash'; import { processEvent } from '../../../course-home/data/thunks'; import { useEventListener } from '../../../generic/hooks'; import { useModel } from '../../../generic/model-store'; @@ -113,6 +114,25 @@ const Unit = ({ } }, [userNeedsIntegritySignature]); + // Send visibility status to the iframe. It's used to mark XBlocks as viewed. + const updateIframeVisibility = () => { + const iframeElement = document.getElementById('unit-iframe'); + if (iframeElement) { + const rect = iframeElement.getBoundingClientRect(); + const visibleInfo = { + type: 'unit.visibilityStatus', + data: { + topPosition: rect.top, + viewportHeight: window.innerHeight, + }, + }; + iframeElement.contentWindow.postMessage(visibleInfo, `${getConfig().LMS_BASE_URL}`); + } + }; + + // Throttle the update function to prevent it from sending too many messages to the iframe. + const throttledUpdateVisibility = throttle(updateIframeVisibility, 100); + const receiveMessage = useCallback(({ data }) => { const { type, @@ -233,6 +253,13 @@ const Unit = ({ dispatch(processEvent(e.data, fetchCourse)); } }; + + // Update the visibility of the iframe in case the element is already visible. + updateIframeVisibility(); + + // Add event listeners to update the visibility of the iframe when the window is scrolled or resized. + window.addEventListener('scroll', throttledUpdateVisibility); + window.addEventListener('resize', throttledUpdateVisibility); }} />