From 6d42ee9c6f26ce1d396422a96456d9235158dd71 Mon Sep 17 00:00:00 2001 From: Kshitij Sobti Date: Wed, 13 Apr 2022 19:31:29 +0530 Subject: [PATCH] feat: add discussions tab [BD-38] [TNL-9743] (#879) * feat: add discussions tab Adds code to load the discussions MFE in an iframe in the tab so the user isn't redirected to the LMS. Adds code for the discussions tab, making it dynamically resize based on contents using a postMessage API. * feat: update path based on user navigation inside discussions MFE The discussions MFE will send path change events via the postMessage API so that the learning MFE path can be kept in sync. This will allow reloading a page without having the iframe revert to same path each time. --- src/course-home/data/thunks.js | 56 +++++++-------- .../discussion-tab/DiscussionTab.jsx | 36 ++++++++++ .../discussion-tab/DiscussionTab.test.jsx | 61 ++++++++++++++++ .../course/sidebar/common/SidebarBase.jsx | 3 +- .../discussions/DiscussionsSidebar.jsx | 4 +- .../discussions/DiscussionsSidebar.test.jsx | 69 +++++++++++++++++++ src/generic/hooks.js | 42 ++++++++++- src/generic/hooks.test.jsx | 45 ++++++++++++ src/index.jsx | 7 ++ 9 files changed, 288 insertions(+), 35 deletions(-) create mode 100644 src/course-home/discussion-tab/DiscussionTab.jsx create mode 100644 src/course-home/discussion-tab/DiscussionTab.test.jsx create mode 100644 src/courseware/course/sidebar/sidebars/discussions/DiscussionsSidebar.test.jsx create mode 100644 src/generic/hooks.test.jsx diff --git a/src/course-home/data/thunks.js b/src/course-home/data/thunks.js index e1ca82a0..8d65976b 100644 --- a/src/course-home/data/thunks.js +++ b/src/course-home/data/thunks.js @@ -32,46 +32,38 @@ const eventTypes = { export function fetchTab(courseId, tab, getTabData, targetUserId) { return async (dispatch) => { dispatch(fetchTabRequest({ courseId })); - Promise.allSettled([ - getCourseHomeCourseMetadata(courseId, 'outline'), - getTabData(courseId, targetUserId), - ]).then(([courseHomeCourseMetadataResult, tabDataResult]) => { - const fetchedCourseHomeCourseMetadata = courseHomeCourseMetadataResult.status === 'fulfilled'; - const fetchedTabData = tabDataResult.status === 'fulfilled'; - - if (fetchedCourseHomeCourseMetadata) { - dispatch(addModel({ - modelType: 'courseHomeMeta', - model: { - id: courseId, - ...courseHomeCourseMetadataResult.value, - }, - })); - } else { - logError(courseHomeCourseMetadataResult.reason); - } - - if (fetchedTabData) { + try { + const courseHomeCourseMetadata = await getCourseHomeCourseMetadata(courseId, 'outline'); + dispatch(addModel({ + modelType: 'courseHomeMeta', + model: { + id: courseId, + ...courseHomeCourseMetadata, + }, + })); + const tabDataResult = getTabData && await getTabData(courseId, targetUserId); + if (tabDataResult) { dispatch(addModel({ modelType: tab, model: { id: courseId, - ...tabDataResult.value, + ...tabDataResult, }, })); - } else { - logError(tabDataResult.reason); } - // Disable the access-denied path for now - it caused a regression - if (fetchedCourseHomeCourseMetadata && !courseHomeCourseMetadataResult.value.courseAccess.hasAccess) { + if (!courseHomeCourseMetadata.courseAccess.hasAccess) { dispatch(fetchTabDenied({ courseId })); - } else if (fetchedCourseHomeCourseMetadata && fetchedTabData) { - dispatch(fetchTabSuccess({ courseId, targetUserId })); - } else { - dispatch(fetchTabFailure({ courseId })); + } else if (tabDataResult || !getTabData) { + dispatch(fetchTabSuccess({ + courseId, + targetUserId, + })); } - }); + } catch (e) { + dispatch(fetchTabFailure({ courseId })); + logError(e); + } }; } @@ -87,6 +79,10 @@ export function fetchOutlineTab(courseId) { return fetchTab(courseId, 'outline', getOutlineTabData); } +export function fetchDiscussionTab(courseId) { + return fetchTab(courseId, 'discussion'); +} + export function dismissWelcomeMessage(courseId) { return async () => postDismissWelcomeMessage(courseId); } diff --git a/src/course-home/discussion-tab/DiscussionTab.jsx b/src/course-home/discussion-tab/DiscussionTab.jsx new file mode 100644 index 00000000..d4d2d6e9 --- /dev/null +++ b/src/course-home/discussion-tab/DiscussionTab.jsx @@ -0,0 +1,36 @@ +import { getConfig } from '@edx/frontend-platform'; +import { injectIntl } from '@edx/frontend-platform/i18n'; +import React, { useState } from 'react'; +import { useSelector } from 'react-redux'; +import { generatePath, useHistory } from 'react-router'; +import { useParams } from 'react-router-dom'; +import { useIFrameHeight, useIFramePluginEvents } from '../../generic/hooks'; + +function DiscussionTab() { + const { courseId } = useSelector(state => state.courseHome); + const { path } = useParams(); + const [originalPath] = useState(path); + const history = useHistory(); + + const [, iFrameHeight] = useIFrameHeight(); + useIFramePluginEvents({ + 'discussions.navigate': (payload) => { + const basePath = generatePath('/course/:courseId/discussion', { courseId }); + history.push(`${basePath}/${payload.path}`); + }, + }); + const discussionsUrl = `${getConfig().DISCUSSIONS_MFE_BASE_URL}/${courseId}/${originalPath}`; + return ( +