From 42da73538dd0d623cf3cd356b7f0016fad4fb9d7 Mon Sep 17 00:00:00 2001 From: Peter Kulko Date: Thu, 24 Oct 2024 19:43:49 +0300 Subject: [PATCH] feat: new edit modals --- src/course-unit/CourseUnit.jsx | 18 ++-------- src/course-unit/constants.js | 1 + src/course-unit/data/api.js | 13 ------- src/course-unit/data/thunk.js | 25 ------------- src/course-unit/hooks.jsx | 6 ---- src/course-unit/sidebar/PublishControls.jsx | 2 ++ .../xblock-container-iframe/index.tsx | 36 ++++++++++++++----- src/editors/hooks.js | 3 ++ 8 files changed, 36 insertions(+), 68 deletions(-) diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index 27e58793a9..74b2a20807 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -1,4 +1,4 @@ -import { useEffect, useState, useMemo } from 'react'; +import { useEffect } from 'react'; import PropTypes from 'prop-types'; import { useSelector } from 'react-redux'; import { useParams } from 'react-router-dom'; @@ -60,7 +60,6 @@ const CourseUnit = ({ courseId }) => { handleCreateNewCourseXBlock, handleConfigureSubmit, courseVerticalChildren, - handleXBlockDragAndDrop, canPasteComponent, isMoveModalOpen, openMoveModal, @@ -70,18 +69,11 @@ const CourseUnit = ({ courseId }) => { handleCloseXBlockMovedAlert, handleNavigateToTargetUnit, } = useCourseUnit({ courseId, blockId }); - const initialXBlocksData = useMemo(() => courseVerticalChildren.children ?? [], [courseVerticalChildren.children]); - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const [unitXBlocks, setUnitXBlocks] = useState(initialXBlocksData); useEffect(() => { document.title = getPageHeadTitle('', unitTitle); }, [unitTitle]); - useEffect(() => { - setUnitXBlocks(courseVerticalChildren.children); - }, [courseVerticalChildren.children]); - const { isShow: isShowProcessingNotification, title: processingNotificationTitle, @@ -99,12 +91,6 @@ const CourseUnit = ({ courseId }) => { ); } - const finalizeXBlockOrder = () => (newXBlocks) => { - handleXBlockDragAndDrop(newXBlocks.map(xBlock => xBlock.id), () => { - setUnitXBlocks(initialXBlocksData); - }); - }; - return ( <> @@ -193,11 +179,11 @@ const CourseUnit = ({ courseId }) => { /> )} } - A promise that resolves to the updated data after setting the XBlock order. - */ -export async function setXBlockOrderList(blockId, children) { - const { data } = await getAuthenticatedHttpClient() - .put(getXBlockBaseApiUrl(blockId), { children }); - - return data; -} - /** * Get an object containing course outline data. * @param {string} courseId - The identifier of the course. diff --git a/src/course-unit/data/thunk.js b/src/course-unit/data/thunk.js index b4f599020a..a21a0c6f21 100644 --- a/src/course-unit/data/thunk.js +++ b/src/course-unit/data/thunk.js @@ -18,7 +18,6 @@ import { handleCourseUnitVisibilityAndData, deleteUnitItem, duplicateUnitItem, - setXBlockOrderList, getCourseOutlineInfo, patchUnitItem, } from './api'; @@ -48,7 +47,6 @@ export function fetchCourseUnitQuery(courseId) { try { const courseUnit = await getCourseUnitData(courseId); - console.log('courseUnit =========>', courseUnit); dispatch(fetchCourseItemSuccess(courseUnit)); dispatch(updateLoadingCourseUnitStatus({ status: RequestStatus.SUCCESSFUL })); return true; @@ -247,29 +245,6 @@ export function duplicateUnitItemQuery(itemId, xblockId) { }; } -export function setXBlockOrderListQuery(blockId, xblockListIds, restoreCallback) { - return async (dispatch) => { - dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); - dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving)); - - try { - await setXBlockOrderList(blockId, xblockListIds).then(async (result) => { - if (result) { - // dispatch(reorderXBlockList(xblockListIds)); - dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); - const courseUnit = await getCourseUnitData(blockId); - dispatch(fetchCourseItemSuccess(courseUnit)); - } - }); - } catch (error) { - restoreCallback(); - handleResponseErrors(error, dispatch, updateSavingStatus); - } finally { - dispatch(hideProcessingNotification()); - } - }; -} - export function getCourseOutlineInfoQuery(courseId) { return async (dispatch) => { dispatch(updateCourseOutlineInfoLoadingStatus({ status: RequestStatus.IN_PROGRESS })); diff --git a/src/course-unit/hooks.jsx b/src/course-unit/hooks.jsx index 65a4ceeceb..bf8b657ca2 100644 --- a/src/course-unit/hooks.jsx +++ b/src/course-unit/hooks.jsx @@ -14,7 +14,6 @@ import { fetchCourseVerticalChildrenData, deleteUnitItemQuery, duplicateUnitItemQuery, - setXBlockOrderListQuery, editCourseUnitVisibilityAndData, getCourseOutlineInfoQuery, patchUnitItemQuery, @@ -121,10 +120,6 @@ export const useCourseUnit = ({ courseId, blockId }) => { }, }; - const handleXBlockDragAndDrop = (xblockListIds, restoreCallback) => { - dispatch(setXBlockOrderListQuery(blockId, xblockListIds, restoreCallback)); - }; - const handleRollbackMovedXBlock = () => { const { sourceLocator, targetParentLocator, title, currentParentLocator, @@ -193,7 +188,6 @@ export const useCourseUnit = ({ courseId, blockId }) => { handleCreateNewCourseXBlock, handleConfigureSubmit, courseVerticalChildren, - handleXBlockDragAndDrop, canPasteComponent, isMoveModalOpen, openMoveModal, diff --git a/src/course-unit/sidebar/PublishControls.jsx b/src/course-unit/sidebar/PublishControls.jsx index c7e787a8e1..0ef08baf28 100644 --- a/src/course-unit/sidebar/PublishControls.jsx +++ b/src/course-unit/sidebar/PublishControls.jsx @@ -36,6 +36,8 @@ const PublishControls = ({ blockId }) => { const handleCourseUnitDiscardChanges = () => { closeDiscardModal(); dispatch(editCourseUnitVisibilityAndData(blockId, PUBLISH_TYPES.discardChanges)); + // TODO: this artificial delay is a temporary solution + // to ensure the iframe content is properly refreshed. setTimeout(() => { sendMessageToIframe(messageTypes.refreshXBlock, null); }, 1000); diff --git a/src/course-unit/xblock-container-iframe/index.tsx b/src/course-unit/xblock-container-iframe/index.tsx index 66f3655ac1..39b8705d0d 100644 --- a/src/course-unit/xblock-container-iframe/index.tsx +++ b/src/course-unit/xblock-container-iframe/index.tsx @@ -5,6 +5,7 @@ import { useIntl } from '@edx/frontend-platform/i18n'; import { getConfig } from '@edx/frontend-platform'; import { useToggle } from '@openedx/paragon'; import { useDispatch } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; import DeleteModal from '../../generic/delete-modal/DeleteModal'; import ConfigureModal from '../../generic/configure-modal/ConfigureModal'; @@ -19,6 +20,7 @@ import messages from './messages'; const IFRAME_BOTTOM_OFFSET = 220; interface XBlockContainerIframeProps { + courseId: string; blockId: string; unitXBlockActions: { handleDelete: (XBlockId: string) => void; @@ -60,18 +62,19 @@ interface XBlockContainerIframeProps { } const XBlockContainerIframe: FC = ({ - blockId, unitXBlockActions, xblocks, handleConfigureSubmit, finalizeXBlockOrder, + courseId, blockId, unitXBlockActions, xblocks, handleConfigureSubmit, }) => { const intl = useIntl(); const iframeRef = useRef(null); const dispatch = useDispatch(); - const { setIframeRef, sendMessageToIframe } = useIframe(); + const navigate = useNavigate(); const [deleteXblockId, setDeleteXblockId] = useState(null); - const [editXblockId, setEditXblockId] = useState(null); - const [currentXblockData, setCurrentXblockData] = useState>({}); const [isDeleteModalOpen, openDeleteModal, closeDeleteModal] = useToggle(false); const [isConfigureModalOpen, openConfigureModal, closeConfigureModal] = useToggle(false); + const { setIframeRef, sendMessageToIframe } = useIframe(); + const [editXblockId, setEditXblockId] = useState(null); + const [currentXblockData, setCurrentXblockData] = useState>({}); const iframeUrl = `${getConfig().STUDIO_BASE_URL}/container_embed/${blockId}`; @@ -87,13 +90,16 @@ const XBlockContainerIframe: FC = ({ const handleConfigure = (id: string) => { openConfigureModal(); setEditXblockId(id); - const foundBlock = xblocks?.find(block => block.blockId === id); - if (foundBlock) { + const foundXBlockInfo = xblocks?.find(block => block.blockId === id); + + if (foundXBlockInfo) { + const { name, userPartitionInfo } = foundXBlockInfo; + setCurrentXblockData({ category: COURSE_BLOCK_NAMES.component.id, - displayName: foundBlock.name, - userPartitionInfo: foundBlock.userPartitionInfo, + displayName: name, + userPartitionInfo, showCorrectness: 'always', }); } @@ -106,6 +112,8 @@ const XBlockContainerIframe: FC = ({ const handleDuplicateXBlock = (id) => { if (id) { unitXBlockActions.handleDuplicate(id); + // TODO: this artificial delay is a temporary solution + // to ensure the iframe content is properly refreshed. setTimeout(() => { sendMessageToIframe(messageTypes.refreshXBlock, null); }, 1000); @@ -113,11 +121,17 @@ const XBlockContainerIframe: FC = ({ }; const handleRefreshXBlocks = () => { + // TODO: this artificial delay is a temporary solution + // to ensure the iframe content is properly refreshed. setTimeout(() => { dispatch(fetchCourseUnitQuery(blockId)); }, 1000); }; + const navigateToNewXBlockEditor = (url: string) => { + navigate(`/course/${courseId}/editor${url}`); + }; + useEffect(() => { const messageHandlers: Record void> = { [messageTypes.deleteXBlock]: (payload) => handleDelete(payload.id), @@ -125,10 +139,12 @@ const XBlockContainerIframe: FC = ({ [messageTypes.copyXBlock]: (payload) => handleCopy(payload.id), [messageTypes.duplicateXBlock]: (payload) => handleDuplicateXBlock(payload.id), [messageTypes.refreshPositions]: handleRefreshXBlocks, + [messageTypes.newXBlockEditor]: (payload) => navigateToNewXBlockEditor(payload.url), }; const handleMessage = (event: MessageEvent) => { const { type, payload } = event.data || {}; + console.log('MESSAGE FROM IFRAME =================>', { type, payload }); if (type && messageHandlers[type]) { messageHandlers[type](payload); } @@ -150,6 +166,8 @@ const XBlockContainerIframe: FC = ({ if (deleteXblockId) { unitXBlockActions.handleDelete(deleteXblockId); closeDeleteModal(); + // TODO: this artificial delay is a temporary solution + // to ensure the iframe content is properly refreshed. setTimeout(() => { sendMessageToIframe(messageTypes.refreshXBlock, null); }, 1000); @@ -159,6 +177,8 @@ const XBlockContainerIframe: FC = ({ const onConfigureSubmit = (...args: any[]) => { if (editXblockId) { handleConfigureSubmit(editXblockId, ...args, closeConfigureModal); + // TODO: this artificial delay is a temporary solution + // to ensure the iframe content is properly refreshed. setTimeout(() => { sendMessageToIframe(messageTypes.refreshXBlock, null); }, 1000); diff --git a/src/editors/hooks.js b/src/editors/hooks.js index c0d1b70a87..15b981849d 100644 --- a/src/editors/hooks.js +++ b/src/editors/hooks.js @@ -18,6 +18,9 @@ export const initializeApp = ({ dispatch, data }) => useEffect( ); export const navigateTo = (destination) => { + // TODO: once the "Remove backend redirects (use SPA functionality)" PR (#1372) is merged, + // the editor will utilize SPA functionality, allowing navigation back + // to the course unit page without a full page reload. window.location.assign(destination); };