diff --git a/package.json b/package.json index 80d11b8c..92532a32 100644 --- a/package.json +++ b/package.json @@ -21,6 +21,7 @@ "start": "fedx-scripts webpack-dev-server --progress", "dev": "PUBLIC_PATH=/learning/ MFE_CONFIG_API_URL='http://localhost:8000/api/mfe_config/v1' fedx-scripts webpack-dev-server --progress --host apps.local.openedx.io", "test": "fedx-scripts jest --coverage --passWithNoTests", + "test:watch": "fedx-scripts jest --watch --passWithNoTests", "types": "tsc --noEmit" }, "author": "edX", diff --git a/src/constants.ts b/src/constants.ts index 20f7d35d..06dc4f86 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -55,6 +55,13 @@ export const AUDIT_MODES = [ 'unpaid-bootcamp', ] as const satisfies readonly string[]; +// In sync with CourseMode.UPSELL_TO_VERIFIED_MODES +// https://github.com/openedx/edx-platform/blob/master/common/djangoapps/course_modes/models.py#L231 +export const ALLOW_UPSELL_MODES = [ + 'audit', + 'honor', +] as const satisfies readonly string[]; + export const WIDGETS = { DISCUSSIONS: 'DISCUSSIONS', NOTIFICATIONS: 'NOTIFICATIONS', diff --git a/src/courseware/course/chat/Chat.jsx b/src/courseware/course/chat/Chat.jsx index 702d76e7..86412c46 100644 --- a/src/courseware/course/chat/Chat.jsx +++ b/src/courseware/course/chat/Chat.jsx @@ -6,7 +6,7 @@ import { Xpert } from '@edx/frontend-lib-learning-assistant'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl } from '@edx/frontend-platform/i18n'; -import { AUDIT_MODES, VERIFIED_MODES } from '@src/constants'; +import { ALLOW_UPSELL_MODES, VERIFIED_MODES } from '@src/constants'; import { useModel } from '../../../generic/model-store'; const Chat = ({ @@ -22,67 +22,48 @@ const Chat = ({ } = useSelector(state => state.specialExams); const course = useModel('coursewareMeta', courseId); + // If is disabled or taking an exam, we don't show the chat. + if (!enabled || activeAttempt?.attempt_id || exam?.id) { return null; } + + // If is not staff and doesn't have an enrollment, we don't show the chat. + if (!isStaff && !enrollmentMode) { return null; } + + const verifiedMode = VERIFIED_MODES.includes(enrollmentMode); // Enrollment verified + const auditMode = ( + !isStaff + && !verifiedMode + && ALLOW_UPSELL_MODES.includes(enrollmentMode) // Can upgrade course + && getConfig().ENABLE_XPERT_AUDIT + ); + // If user has no access, we don't show the chat. + if (!isStaff && !(verifiedMode || auditMode)) { return null; } + + // Date validation const { accessExpiration, start, end, } = course; - const hasVerifiedEnrollment = ( - enrollmentMode !== null - && enrollmentMode !== undefined - && VERIFIED_MODES.includes(enrollmentMode) + const utcDate = (new Date()).toISOString(); + const expiration = accessExpiration?.expirationDate || utcDate; + const validDate = ( + (start ? start <= utcDate : true) + && (end ? end >= utcDate : true) + && (auditMode ? expiration >= utcDate : true) ); + // If date is invalid, we don't show the chat. + if (!validDate) { return null; } - // audit learners should only have access if the ENABLE_XPERT_AUDIT setting is true - const hasAuditEnrollmentAndAccess = ( - enrollmentMode !== null - && enrollmentMode !== undefined - && AUDIT_MODES.includes(enrollmentMode) - && getConfig().ENABLE_XPERT_AUDIT - ); - - const validDates = () => { - const date = new Date(); - const utcDate = date.toISOString(); - - const startDate = start || utcDate; - const endDate = end || utcDate; - const accessExpirationDate = accessExpiration && accessExpiration.expirationDate - ? accessExpiration.expirationDate : utcDate; - - return ( - startDate <= utcDate - && utcDate <= endDate - && (hasAuditEnrollmentAndAccess ? utcDate <= accessExpirationDate : true) - ); - }; - - const shouldDisplayChat = ( - enabled - && (hasVerifiedEnrollment || isStaff || hasAuditEnrollmentAndAccess) - && validDates() - // it is necessary to check both whether the user is in an exam, and whether or not they are viewing an exam - // this will prevent the learner from interacting with the tool at any point of the exam flow, even at the - // entrance interstitial. - && !(activeAttempt?.attempt_id || exam?.id) - ); - - const isUpgradeEligible = !hasVerifiedEnrollment && !isStaff; - - return ( - <> - {/* Use a portal to ensure that component overlay does not compete with learning MFE styles. */} - {shouldDisplayChat && (createPortal( - , - document.body, - ))} - + // Use a portal to ensure that component overlay does not compete with learning MFE styles. + return createPortal( + , + document.body, ); };