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,
);
};