Merge PR #50 banner/lock-access
* Commits: Add audit access locked banner
This commit is contained in:
@@ -150,6 +150,7 @@ function Sequence({
|
||||
)}
|
||||
{!gated && unitId !== null && (
|
||||
<Unit
|
||||
courseId={courseId}
|
||||
key={unitId}
|
||||
id={unitId}
|
||||
onLoaded={handleUnitLoaded}
|
||||
|
||||
@@ -1,12 +1,24 @@
|
||||
import React, { useRef, useEffect, useState } from 'react';
|
||||
import React, {
|
||||
Suspense,
|
||||
useRef,
|
||||
useEffect,
|
||||
useState,
|
||||
} from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
import messages from './messages';
|
||||
import BookmarkButton from '../bookmark/BookmarkButton';
|
||||
import { useModel } from '../../../model-store';
|
||||
import PageLoading from '../../../PageLoading';
|
||||
|
||||
export default function Unit({
|
||||
const LockPaywall = React.lazy(() => import('./lock-paywall'));
|
||||
|
||||
function Unit({
|
||||
courseId,
|
||||
onLoaded,
|
||||
id,
|
||||
intl,
|
||||
}) {
|
||||
const iframeRef = useRef(null);
|
||||
const iframeUrl = `${getConfig().LMS_BASE_URL}/xblock/${id}?show_title=0&show_bookmark_button=0`;
|
||||
@@ -15,6 +27,11 @@ export default function Unit({
|
||||
const [hasLoaded, setHasLoaded] = useState(false);
|
||||
|
||||
const unit = useModel('units', id);
|
||||
const course = useModel('courses', courseId);
|
||||
const {
|
||||
contentTypeGatingEnabled,
|
||||
enrollmentMode,
|
||||
} = course;
|
||||
|
||||
useEffect(() => {
|
||||
global.onmessage = (event) => {
|
||||
@@ -40,6 +57,19 @@ export default function Unit({
|
||||
isBookmarked={unit.bookmarked}
|
||||
isProcessing={unit.bookmarkedUpdateState === 'loading'}
|
||||
/>
|
||||
{ contentTypeGatingEnabled && unit.graded && enrollmentMode === 'audit' && (
|
||||
<Suspense
|
||||
fallback={(
|
||||
<PageLoading
|
||||
srMessage={intl.formatMessage(messages['learn.loading.content.lock'])}
|
||||
/>
|
||||
)}
|
||||
>
|
||||
<LockPaywall
|
||||
courseId={courseId}
|
||||
/>
|
||||
</Suspense>
|
||||
)}
|
||||
<div className="unit-iframe-wrapper">
|
||||
<iframe
|
||||
id="unit-iframe"
|
||||
@@ -57,10 +87,14 @@ export default function Unit({
|
||||
}
|
||||
|
||||
Unit.propTypes = {
|
||||
courseId: PropTypes.string.isRequired,
|
||||
id: PropTypes.string.isRequired,
|
||||
intl: intlShape.isRequired,
|
||||
onLoaded: PropTypes.func,
|
||||
};
|
||||
|
||||
Unit.defaultProps = {
|
||||
onLoaded: undefined,
|
||||
};
|
||||
|
||||
export default injectIntl(Unit);
|
||||
|
||||
61
src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
Normal file
61
src/courseware/course/sequence/lock-paywall/LockPaywall.jsx
Normal file
@@ -0,0 +1,61 @@
|
||||
import React from 'react';
|
||||
import PropTypes from 'prop-types';
|
||||
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
|
||||
import { faLock } from '@fortawesome/free-solid-svg-icons';
|
||||
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
|
||||
|
||||
import messages from './messages';
|
||||
import VerifiedCert from './assets/edx-verified-mini-cert.png';
|
||||
import { useModel } from '../../../../model-store';
|
||||
|
||||
function LockPaywall({
|
||||
intl,
|
||||
courseId,
|
||||
}) {
|
||||
const course = useModel('courses', courseId);
|
||||
const {
|
||||
verifiedMode,
|
||||
} = course;
|
||||
|
||||
if (!verifiedMode) {
|
||||
return null;
|
||||
}
|
||||
const {
|
||||
currencySymbol,
|
||||
price,
|
||||
upgradeUrl,
|
||||
} = verifiedMode;
|
||||
return (
|
||||
<div className="border border-gray rounded d-flex justify-content-between mt-2 p-3">
|
||||
<div>
|
||||
<h4 className="font-weight-bold mb-2">
|
||||
<FontAwesomeIcon icon={faLock} className="text-black mr-2 ml-1" style={{ fontSize: '2rem' }} />
|
||||
<span>{intl.formatMessage(messages['learn.lockPaywall.title'])}</span>
|
||||
</h4>
|
||||
<p className="mb-0">
|
||||
<span>{intl.formatMessage(messages['learn.lockPaywall.content'])}</span>
|
||||
|
||||
<a href={upgradeUrl}>
|
||||
{intl.formatMessage(messages['learn.lockPaywall.upgrade.link'], {
|
||||
currencySymbol,
|
||||
price,
|
||||
})}
|
||||
</a>
|
||||
</p>
|
||||
</div>
|
||||
<div>
|
||||
<img
|
||||
alt={intl.formatMessage(messages['learn.lockPaywall.example.alt'])}
|
||||
src={VerifiedCert}
|
||||
className="border-0"
|
||||
style={{ height: '70px' }}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
LockPaywall.propTypes = {
|
||||
intl: intlShape.isRequired,
|
||||
courseId: PropTypes.string.isRequired,
|
||||
};
|
||||
export default injectIntl(LockPaywall);
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 8.9 KiB |
1
src/courseware/course/sequence/lock-paywall/index.js
Normal file
1
src/courseware/course/sequence/lock-paywall/index.js
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from './LockPaywall';
|
||||
26
src/courseware/course/sequence/lock-paywall/messages.js
Normal file
26
src/courseware/course/sequence/lock-paywall/messages.js
Normal file
@@ -0,0 +1,26 @@
|
||||
import { defineMessages } from '@edx/frontend-platform/i18n';
|
||||
|
||||
const messages = defineMessages({
|
||||
'learn.lockPaywall.title': {
|
||||
id: 'learn.lockPaywall.title',
|
||||
defaultMessage: 'Verified Track Access',
|
||||
description: 'Heading for message shown to indicate that a piece of content is unavailable to audit track users.',
|
||||
},
|
||||
'learn.lockPaywall.content': {
|
||||
id: 'learn.lockPaywall.content',
|
||||
defaultMessage: 'Graded assessments are available to Verified Track learners.',
|
||||
description: 'Message shown to indicate that a piece of content is unavailable to audit track users.',
|
||||
},
|
||||
'learn.lockPaywall.upgrade.link': {
|
||||
id: 'learn.lockPaywall.upgrade.link',
|
||||
defaultMessage: 'Upgrade to unlock ({currencySymbol}{price})',
|
||||
description: 'A link users can click that navigates their browser to the upgrade payment page.',
|
||||
},
|
||||
'learn.lockPaywall.example.alt': {
|
||||
id: 'learn.lockPaywall.example.alt',
|
||||
defaultMessage: 'Example Certificate',
|
||||
description: 'Alternate text displayed when the example certificate image cannot be displayed.',
|
||||
},
|
||||
});
|
||||
|
||||
export default messages;
|
||||
@@ -5,6 +5,7 @@ import { logError } from '@edx/frontend-platform/logging';
|
||||
|
||||
function normalizeMetadata(metadata) {
|
||||
return {
|
||||
contentTypeGatingEnabled: metadata.content_type_gating_enabled,
|
||||
// TODO: TNL-7185: return course expired _date_, instead of _message_
|
||||
courseExpiredMessage: metadata.course_expired_message,
|
||||
id: metadata.id,
|
||||
@@ -66,6 +67,7 @@ function normalizeBlocks(courseId, blocks) {
|
||||
break;
|
||||
case 'vertical':
|
||||
models.units[block.id] = {
|
||||
graded: block.graded,
|
||||
id: block.id,
|
||||
title: block.display_name,
|
||||
lmsWebUrl: block.lms_web_url,
|
||||
@@ -112,7 +114,7 @@ export async function getCourseBlocks(courseId) {
|
||||
url.searchParams.append('course_id', courseId);
|
||||
url.searchParams.append('username', username);
|
||||
url.searchParams.append('depth', 3);
|
||||
url.searchParams.append('requested_fields', 'children,show_gated_sections');
|
||||
url.searchParams.append('requested_fields', 'children,show_gated_sections,graded');
|
||||
|
||||
const { data } = await getAuthenticatedHttpClient().get(url.href, {});
|
||||
return normalizeBlocks(courseId, data.blocks);
|
||||
|
||||
Reference in New Issue
Block a user