Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .env
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,6 @@ TWITTER_URL=''
USER_INFO_COOKIE_NAME=''
OPTIMIZELY_FULL_STACK_SDK_KEY=''
SHOW_UNGRADED_ASSIGNMENT_PROGRESS=''
FEATURE_ENABLE_CHAT_V2_ENDPOINT=''
# Fallback in local style files
PARAGON_THEME_URLS={}

6 changes: 3 additions & 3 deletions .env.development
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ NODE_ENV='development'

ACCESS_TOKEN_COOKIE_NAME='edx-jwt-cookie-header-payload'
APP_ID='learning'
BASE_URL='http://localhost:2000'
BASE_URL='http://localhost:2010'
CONTACT_URL='http://localhost:18000/contact'
CREDENTIALS_BASE_URL='http://localhost:18150'
CREDIT_HELP_LINK_URL='https://help.edx.org/edxlearner/s/article/Can-I-receive-college-credit-or-credit-hours-for-my-course'
Expand All @@ -31,7 +31,7 @@ LOGO_WHITE_URL=https://edx-cdn.org/v3/default/logo-white.svg
LEGACY_THEME_NAME=''
MARKETING_SITE_BASE_URL='http://localhost:18000'
ORDER_HISTORY_URL='http://localhost:1996/orders'
PORT=2000
PORT=2010
PROCTORED_EXAM_FAQ_URL=''
PROCTORED_EXAM_RULES_URL=''
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
Expand All @@ -53,6 +53,6 @@ CHAT_RESPONSE_URL='http://localhost:18000/api/learning_assistant/v1/course_id'
PRIVACY_POLICY_URL='http://localhost:18000/privacy'
OPTIMIZELY_FULL_STACK_SDK_KEY=''
SHOW_UNGRADED_ASSIGNMENT_PROGRESS=''
FEATURE_ENABLE_CHAT_V2_ENDPOINT='false'
ENABLE_XPERT_AUDIT='true'
# Fallback in local style files
PARAGON_THEME_URLS={}
76 changes: 23 additions & 53 deletions src/courseware/course/chat/Chat.jsx
Original file line number Diff line number Diff line change
@@ -1,66 +1,37 @@
import { createPortal } from 'react-dom';
import { useSelector } from 'react-redux';
import PropTypes from 'prop-types';

import { Xpert } from '@edx/frontend-lib-learning-assistant';
import { getConfig } from '@edx/frontend-platform';

import { ALLOW_UPSELL_MODES, VERIFIED_MODES } from '@src/constants';
import { useModel } from '../../../generic/model-store';
import { PluginSlot } from '@openedx/frontend-plugin-framework';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';

const Chat = ({
enabled,
enrollmentMode,
isStaff,
courseId,
contentToolsEnabled,
unitId,
}) => {
const {
activeAttempt, exam,
} = 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 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; }

// Use a portal to ensure that component overlay does not compete with learning MFE styles.
const { userId } = getAuthenticatedUser();

// If chat is disabled, don't show anything
if (!enabled) {
return null;
}

// Provide minimal, generic context - no feature-specific flags
const pluginContext = {
courseId,
unitId,
userId,
isStaff,
enrollmentMode,
};

// Use generic plugin slot ID (location-based, not feature-specific)
// Plugins will query their own requirements from Redux/config
return createPortal(
<Xpert
courseId={courseId}
contentToolsEnabled={contentToolsEnabled}
unitId={unitId}
isUpgradeEligible={auditMode}
<PluginSlot
id="learner_tools_slot"
pluginProps={pluginContext}
/>,
document.body,
);
Expand All @@ -71,7 +42,6 @@ Chat.propTypes = {
enabled: PropTypes.bool.isRequired,
enrollmentMode: PropTypes.string,
courseId: PropTypes.string.isRequired,
contentToolsEnabled: PropTypes.bool.isRequired,
unitId: PropTypes.string.isRequired,
};

Expand Down
Loading