Compare commits

..

3 Commits

Author SHA1 Message Date
Dillon Dumesnil
48ae83084b Adding images 2020-12-01 13:21:05 +00:00
Dillon Dumesnil
6d524f535d Use apostrophe instead of single quote. 2020-11-24 13:46:42 +00:00
Dillon Dumesnil
cb1ea7ad9f AA-303: Celebrate a user's first ever discussion post! 2020-11-20 16:04:15 +00:00
24 changed files with 231 additions and 121 deletions

7
.env
View File

@@ -15,12 +15,7 @@ ORDER_HISTORY_URL=null
REFRESH_ACCESS_TOKEN_ENDPOINT=null
SEGMENT_KEY=null
SITE_NAME=null
SOCIAL_UTM_MILESTONE_CAMPAIGN=null
TWITTER_URL=null
STUDIO_BASE_URL=
SUPPORT_URL=null
SUPPORT_URL_CALCULATOR_MATH=null
SUPPORT_URL_ID_VERIFICATION=null
SUPPORT_URL_VERIFIED_CERTIFICATE=null
TWITTER_HASHTAG=null
TWITTER_URL=null
USER_INFO_COOKIE_NAME=null

View File

@@ -15,12 +15,7 @@ PORT=2000
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SOCIAL_UTM_MILESTONE_CAMPAIGN='edxmilestone'
TWITTER_URL='https://twitter.com/edXOnline'
STUDIO_BASE_URL='http://localhost:18010'
SUPPORT_URL='https://support.edx.org'
SUPPORT_URL_CALCULATOR_MATH='https://support.edx.org/hc/en-us/articles/360000038428-Entering-math-expressions-in-assignments-or-the-calculator'
SUPPORT_URL_ID_VERIFICATION='https://support.edx.org/hc/en-us/articles/206503858-How-do-I-verify-my-identity'
SUPPORT_URL_VERIFIED_CERTIFICATE='https://support.edx.org/hc/en-us/articles/206502008-What-is-a-verified-certificate'
TWITTER_HASHTAG='myedxjourney'
TWITTER_URL='https://twitter.com/edXOnline'
USER_INFO_COOKIE_NAME='edx-user-info'

View File

@@ -14,12 +14,7 @@ PORT=2000
REFRESH_ACCESS_TOKEN_ENDPOINT='http://localhost:18000/login_refresh'
SEGMENT_KEY=null
SITE_NAME='edX'
SOCIAL_UTM_MILESTONE_CAMPAIGN='edxmilestone'
TWITTER_URL='https://twitter.com/edXOnline'
STUDIO_BASE_URL='http://localhost:18010'
SUPPORT_URL='https://support.edx.org'
SUPPORT_URL_CALCULATOR_MATH='https://support.edx.org/hc/en-us/articles/360000038428-Entering-math-expressions-in-assignments-or-the-calculator'
SUPPORT_URL_ID_VERIFICATION='https://support.edx.org/hc/en-us/articles/206503858-How-do-I-verify-my-identity'
SUPPORT_URL_VERIFIED_CERTIFICATE='https://support.edx.org/hc/en-us/articles/206502008-What-is-a-verified-certificate'
TWITTER_HASHTAG='myedxjourney'
TWITTER_URL='https://twitter.com/edXOnline'
USER_INFO_COOKIE_NAME='edx-user-info'

View File

@@ -18,7 +18,7 @@ const messages = defineMessages({
},
success: {
id: 'learning.enrollment.success',
defaultMessage: "You've successfully enrolled in this course!",
defaultMessage: 'Youve successfully enrolled in this course!',
description: 'A message telling the user that their course enrollment was successful.',
},
});

View File

@@ -3,7 +3,7 @@ import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
'datesBanner.datesTabInfoBanner.header': {
id: 'datesBanner.datesTabInfoBanner.header',
defaultMessage: "We've built a suggested schedule to help you stay on track. ",
defaultMessage: 'Weve built a suggested schedule to help you stay on track. ',
description: 'Strong text in Dates Tab Info Banner',
},
'datesBanner.datesTabInfoBanner.body': {
@@ -35,8 +35,8 @@ const messages = defineMessages({
},
'datesBanner.upgradeToResetBanner.body': {
id: 'datesBanner.upgradeToResetBanner.body',
defaultMessage: `which means that you are unable to participate in graded assignments. It looks like you missed
some important deadlines based on our suggested schedule. To complete graded assignments as part of this course
defaultMessage: `which means that you are unable to participate in graded assignments. It looks like you missed
some important deadlines based on our suggested schedule. To complete graded assignments as part of this course
and shift the past due assignments into the future, you can upgrade today.`,
description: 'Body in Upgrade To Reset Banner',
},
@@ -52,7 +52,7 @@ const messages = defineMessages({
},
'datesBanner.resetDatesBanner.body': {
id: 'datesBanner.resetDatesBanner.body',
defaultMessage: `To keep yourself on track, you can update this schedule and shift the past due assignments into
defaultMessage: `To keep yourself on track, you can update this schedule and shift the past due assignments into
the future. Dont worry—you wont lose any of the progress youve made when you shift your due dates.`,
description: 'Body in Reset Dates Banner',
},

View File

@@ -10,7 +10,7 @@ import useOfferAlert from '../../alerts/offer-alert';
import Sequence from './sequence';
import { CelebrationModal, shouldCelebrateOnSectionLoad } from './celebration';
import { FirstSectionCelebrationModal, shouldCelebrateOnSectionLoad } from './celebration';
import CourseBreadcrumbs from './CourseBreadcrumbs';
import CourseSock from './course-sock';
import ContentTools from './content-tools';
@@ -78,7 +78,7 @@ function Course({
previousSequenceHandler={previousSequenceHandler}
/>
{celebrationOpen && (
<CelebrationModal
<FirstSectionCelebrationModal
courseId={courseId}
open
/>

View File

@@ -71,9 +71,9 @@ describe('Course', () => {
handleNextSectionCelebration(sequenceId, sequenceId, testData.unitId);
render(<Course {...testData} />, { store: testStore });
const celebrationModal = screen.getByRole('dialog');
expect(celebrationModal).toBeInTheDocument();
expect(getByRole(celebrationModal, 'heading', { name: 'Congratulations!' })).toBeInTheDocument();
const FirstSectionCelebrationModal = screen.getByRole('dialog');
expect(FirstSectionCelebrationModal).toBeInTheDocument();
expect(getByRole(FirstSectionCelebrationModal, 'heading', { name: 'Congratulations!' })).toBeInTheDocument();
});
it('displays upgrade sock', async () => {

View File

@@ -0,0 +1,91 @@
import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Modal } from '@edx/paragon';
import { layoutGenerator } from 'react-break';
import { useDispatch } from 'react-redux';
import DiscussionMobile from './assets/FirstDiscussion_mobile.png';
import DiscussionTablet from './assets/FirstDiscussion_desktop_500.png';
import messages from './messages';
import { recordFirstDiscussionCelebration } from './utils';
import { updateModel } from '../../../generic/model-store';
function FirstDiscussionCelebrationModal({
courseId, firstDiscussionUserBucket, intl, open, ...rest
}) {
const dispatch = useDispatch();
const layout = layoutGenerator({
mobile: 0,
tablet: 400,
});
const OnMobile = layout.is('mobile');
const OnAtLeastTablet = layout.isAtLeast('tablet');
useEffect(() => {
if (open) {
recordFirstDiscussionCelebration(courseId);
}
}, [open]);
let normativeDataBodyText;
// Bucket 2 corresponds to showing normative data in the body text
if (firstDiscussionUserBucket === 2) {
if (Math.random() > 0.5) {
normativeDataBodyText = (<p className="mt-3">{intl.formatMessage(messages.discussionBodyText1)}</p>);
} else {
normativeDataBodyText = (<p className="mt-3">{intl.formatMessage(messages.discussionBodyText2)}</p>);
}
}
console.log('normativeDataBodyText:', normativeDataBodyText);
return (
<Modal
body={(
<>
<p>{intl.formatMessage(messages.conversation)}</p>
<OnMobile>
<img src={DiscussionMobile} alt="" className="img-fluid" />
</OnMobile>
<OnAtLeastTablet>
<img src={DiscussionTablet} alt="" className="img-fluid" />
</OnAtLeastTablet>
{normativeDataBodyText}
</>
)}
closeText={intl.formatMessage(messages.keepItUp)}
onClose={() => {
// Update our local copy of course data from LMS
console.log('updating model');
dispatch(updateModel({
modelType: 'courses',
model: {
id: courseId,
celebrations: {
firstDiscussion: false,
},
},
}));
}}
open={open}
title={intl.formatMessage(messages.firstDiscussionPost)}
{...rest}
/>
);
}
FirstDiscussionCelebrationModal.defaultProps = {
open: false,
firstDiscussionUserBucket: 2,
};
FirstDiscussionCelebrationModal.propTypes = {
courseId: PropTypes.string.isRequired,
firstDiscussionUserBucket: PropTypes.number,
intl: intlShape.isRequired,
open: PropTypes.bool,
};
export default injectIntl(FirstDiscussionCelebrationModal);

View File

@@ -10,7 +10,7 @@ import messages from './messages';
import SocialIcons from '../../social-share/SocialIcons';
import { recordFirstSectionCelebration } from './utils';
function CelebrationModal({
function FirstSectionCelebrationModal({
courseId, intl, open, ...rest
}) {
const layout = layoutGenerator({
@@ -58,14 +58,14 @@ function CelebrationModal({
);
}
CelebrationModal.defaultProps = {
FirstSectionCelebrationModal.defaultProps = {
open: false,
};
CelebrationModal.propTypes = {
FirstSectionCelebrationModal.propTypes = {
courseId: PropTypes.string.isRequired,
intl: intlShape.isRequired,
open: PropTypes.bool,
};
export default injectIntl(CelebrationModal);
export default injectIntl(FirstSectionCelebrationModal);

Binary file not shown.

After

Width:  |  Height:  |  Size: 961 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 742 B

View File

@@ -4,9 +4,7 @@ import { getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
// Does not block on answer
export function postFirstSectionCelebrationComplete(courseId) {
export function postCelebrationComplete(courseId, payload) {
const url = new URL(`${getConfig().LMS_BASE_URL}/api/courseware/celebration/${courseId}`);
getAuthenticatedHttpClient().post(url.href, {
first_section: false,
});
getAuthenticatedHttpClient().post(url.href, payload);
}

View File

@@ -1,2 +1,7 @@
export { default as CelebrationModal } from './CelebrationModal';
export { handleNextSectionCelebration, shouldCelebrateOnSectionLoad } from './utils';
export { default as FirstDiscussionCelebrationModal } from './FirstDiscussionCelebrationModal';
export { default as FirstSectionCelebrationModal } from './FirstSectionCelebrationModal';
export {
handleNextSectionCelebration,
shouldCelebrateOnSectionLoad,
shouldCelebrateOnDiscussionPost,
} from './utils';

View File

@@ -9,20 +9,41 @@ const messages = defineMessages({
id: 'learning.celebration.congrats',
defaultMessage: 'Congratulations!',
},
conversation: {
id: 'learning.celebration.conversation',
defaultMessage: 'Nice job being part of the conversation.',
},
discussionBodyText1: {
id: 'learning.celebration.discussionBodyText1',
defaultMessage: 'Learners who participate in discussions are 10x more likely to complete their course than those who dont.',
},
discussionBodyText2: {
id: 'learning.celebration.discussionBodyText2',
defaultMessage: 'Learners who participate in discussions complete 3x as much course content on average vs. those who dont.',
},
earned: {
id: 'learning.celebration.earned',
defaultMessage: 'You earned it!',
},
emailSubject: {
id: 'learning.celebration.emailSubject',
defaultMessage: "I'm on my way to completing {title} online with {platform}!",
defaultMessage: 'Im on my way to completing {title} online with {platform}!',
description: 'Subject when sharing course progress via email',
},
firstDiscussionPost: {
id: 'learning.celebration.firstDiscussionPost',
defaultMessage: 'First Discussion post!',
},
forward: {
id: 'learning.celebration.forward',
defaultMessage: 'Keep going',
description: 'Button to close celebration dialog and get back to course',
},
keepItUp: {
id: 'learning.celebration.keepItUp',
defaultMessage: 'Keep it up',
description: 'Button to close celebration dialog and get back to course',
},
share: {
id: 'learning.celebration.share',
defaultMessage: 'Take a moment to celebrate and share your progress.',

View File

@@ -1,11 +1,11 @@
import { sendTrackEvent } from '@edx/frontend-platform/analytics';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { postFirstSectionCelebrationComplete } from './data/api';
import { postCelebrationComplete } from './data/api';
import { clearLocalStorage, getLocalStorage, setLocalStorage } from '../../../data/localStorage';
import { updateModel } from '../../../generic/model-store';
const CELEBRATION_LOCAL_STORAGE_KEY = 'CelebrationModal.showOnSectionLoad';
const CELEBRATION_LOCAL_STORAGE_KEY = 'FirstSectionCelebrationModal.showOnSectionLoad';
// Records clicks through the end of a section, so that we can know whether we should celebrate when we finish loading
function handleNextSectionCelebration(sequenceId, nextSequenceId, nextUnitId) {
@@ -16,18 +16,34 @@ function handleNextSectionCelebration(sequenceId, nextSequenceId, nextUnitId) {
});
}
function recordFirstSectionCelebration(courseId) {
// Tell the LMS
postFirstSectionCelebrationComplete(courseId);
// Tell our analytics
function sendCelebrationSegmentEvent(courseId, eventName) {
const { administrator } = getAuthenticatedUser();
sendTrackEvent('edx.ui.lms.celebration.first_section.opened', {
sendTrackEvent(eventName, {
course_id: courseId,
is_staff: administrator,
});
}
function recordFirstSectionCelebration(courseId) {
// Tell the LMS
postCelebrationComplete(courseId, { first_section: false });
// Tell our analytics
sendCelebrationSegmentEvent(courseId, 'edx.ui.lms.celebration.first_section.opened');
}
function recordFirstDiscussionCelebration(courseId) {
/* postCelebrationComplete should start being used once the Discussion MFE exists
and we no longer record the first discussion post in the JS handler in edx-platform
See edx-platform/common/static/common/js/discussion/views/new_post_view.js and
edx-platform/cms/static/common/js/discussion/views/discussion_thread_view.js */
// Tell the LMS
// postCelebrationComplete(courseId, { first_discussion: false });
// Tell our analytics
sendCelebrationSegmentEvent(courseId, 'edx.ui.lms.celebration.first_discussion.opened');
}
// Looks at local storage to see whether we just came from the end of a section.
// Note! This does have side effects (will clear some local storage and may start an api call).
function shouldCelebrateOnSectionLoad(courseId, sequenceId, unitId, celebrateFirstSection, dispatch) {
@@ -63,4 +79,16 @@ function shouldCelebrateOnSectionLoad(courseId, sequenceId, unitId, celebrateFir
return shouldCelebrate;
}
export { handleNextSectionCelebration, recordFirstSectionCelebration, shouldCelebrateOnSectionLoad };
function shouldCelebrateOnDiscussionPost(firstDiscussion, firstDiscussionUserBucket) {
// Bucket 0 === Control group which does not get the discussion celebration.
// That check can be removed when we stop using the flag as an experiment.
return firstDiscussion && firstDiscussionUserBucket !== 0;
}
export {
handleNextSectionCelebration,
recordFirstDiscussionCelebration,
recordFirstSectionCelebration,
shouldCelebrateOnSectionLoad,
shouldCelebrateOnDiscussionPost,
};

View File

@@ -98,13 +98,21 @@ class Calculator extends Component {
<FormattedMessage
tagName="h6"
id="calculator.instructions"
defaultMessage="For detailed information, see the {expressions_link}."
defaultMessage="For detailed information, see {expressions_link} in the {edx_guide}."
values={{
expressions_link: (
<a href={getConfig().SUPPORT_URL_CALCULATOR_MATH}>
<a href="https://edx.readthedocs.io/projects/edx-guide-for-students/en/latest/completing_assignments/SFD_mathformatting.html#math-formatting">
<FormattedMessage
id="calculator.instructions.support.title"
defaultMessage="Help Center"
id="calculator.instructions.expressions.link.title"
defaultMessage="Entering Mathematical and Scientific Expressions"
/>
</a>
),
edx_guide: (
<a href="https://edx-guide-for-students.readthedocs.io/en/latest/index.html">
<FormattedMessage
id="calculator.instructions.edx.guide.link.title"
defaultMessage="edX Guide for Students"
/>
</a>
),

View File

@@ -61,11 +61,12 @@ function CourseCelebration({ intl }) {
{intl.formatMessage(messages.dashboardLink)}
</Hyperlink>
);
const idVerificationSupportLink = getConfig().SUPPORT_URL_ID_VERIFICATION && (
// todo: remove this hardcoded link to edX support
const idVerificationSupportLink = getConfig().SUPPORT_URL && (
<Hyperlink
className="text-gray-700"
style={{ textDecoration: 'underline' }}
destination={getConfig().SUPPORT_URL_ID_VERIFICATION}
destination={`${getConfig().SUPPORT_URL}/hc/en-us/articles/206503858-How-do-I-verify-my-identity`}
>
{intl.formatMessage(messages.idVerificationSupportLink)}
</Hyperlink>
@@ -187,11 +188,12 @@ function CourseCelebration({ intl }) {
values={{ price: verifiedMode.currencySymbol + verifiedMode.price }}
/>
<br />
{getConfig().SUPPORT_URL_VERIFIED_CERTIFICATE && (
{ /* todo: remove this hardcoded link to edX support */ }
{getConfig().SUPPORT_URL && (
<Hyperlink
className="text-gray-700"
style={{ textDecoration: 'underline' }}
destination={getConfig().SUPPORT_URL_VERIFIED_CERTIFICATE}
destination={`${getConfig().SUPPORT_URL}/hc/en-us/articles/206502008-What-is-a-verified-certificate`}
>
{intl.formatMessage(messages.verifiedCertificateSupportLink)}
</Hyperlink>

View File

@@ -42,7 +42,7 @@ const messages = defineMessages({
dashboardLink: {
id: 'courseExit.dashboardLink',
defaultMessage: 'Dashboard',
description: "Link to user's dashboard",
description: 'Link to users dashboard',
},
downloadButton: {
id: 'courseCelebration.downloadButton',
@@ -69,7 +69,7 @@ const messages = defineMessages({
linkedinAddToProfileButton: {
id: 'courseCelebration.linkedinAddToProfileButton',
defaultMessage: 'Add to LinkedIn profile',
description: "Button to add certificate information to the user's LinkedIn profile",
description: 'Button to add certificate information to the users LinkedIn profile',
},
nextButtonComplete: {
id: 'learn.sequence.navigation.complete.button', // for historical reasons
@@ -82,7 +82,7 @@ const messages = defineMessages({
profileLink: {
id: 'courseExit.profileLink',
defaultMessage: 'Profile',
description: "Link to user's profile",
description: 'Link to users profile',
},
requestCertificateBodyText: {
id: 'courseCelebration.requestCertificateBodyText',

View File

@@ -1,6 +1,5 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import { getConfig } from '@edx/frontend-platform';
import { FormattedMessage } from '@edx/frontend-platform/i18n';
import LearnerQuote1 from './assets/learner-quote.png';
import LearnerQuote2 from './assets/learner-quote2.png';
@@ -37,10 +36,7 @@ export default class CourseSock extends Component {
<h2 className="mt-3 mb-4">
<FormattedMessage
id="coursesock.upsell.verifiedcert"
defaultMessage="{siteName} Verified Certificate"
values={{
siteName: getConfig().SITE_NAME,
}}
defaultMessage="edX Verified Certificate"
/>
</h2>
<div className="row flex-row-reverse">
@@ -98,10 +94,7 @@ export default class CourseSock extends Component {
<li>
<FormattedMessage
id="coursesock.upsell.reason4"
defaultMessage="Certificate purchases help {siteName} continue to offer free courses"
values={{
siteName: getConfig().SITE_NAME,
}}
defaultMessage="Certificate purchases help edX continue to offer free courses"
/>
</li>
</ul>
@@ -141,10 +134,7 @@ export default class CourseSock extends Component {
<h3 className="h5">
<FormattedMessage
id="coursesock.upsell.storytitle"
defaultMessage="{siteName} Learner Stories"
values={{
siteName: getConfig().SITE_NAME,
}}
defaultMessage="edX Learner Stories"
/>
</h3>
<div className="media my-3">
@@ -160,11 +150,8 @@ export default class CourseSock extends Component {
&mdash; <FormattedMessage
id="coursesock.upsell.learner"
description="Name of learner"
defaultMessage="{name}, {siteName} Learner"
values={{
name: 'Christina Fong',
siteName: getConfig().SITE_NAME,
}}
defaultMessage="{ name }, edX Learner"
values={{ name: 'Christina Fong' }}
/>
</p>
</div>
@@ -182,11 +169,8 @@ export default class CourseSock extends Component {
&mdash; <FormattedMessage
id="coursesock.upsell.learner"
description="Name of learner"
defaultMessage="{name}, {siteName} Learner"
values={{
name: 'Cheryl Troell',
siteName: getConfig().SITE_NAME,
}}
defaultMessage="{ name }, edX Learner"
values={{ name: 'Cheryl Troell' }}
/>
</p>
</div>

View File

@@ -12,6 +12,7 @@ import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Modal } from '@edx/paragon';
import messages from './messages';
import BookmarkButton from '../bookmark/BookmarkButton';
import { FirstDiscussionCelebrationModal, shouldCelebrateOnDiscussionPost } from '../celebration';
import { useModel } from '../../../generic/model-store';
import PageLoading from '../../../generic/PageLoading';
import { processEvent } from '../../../course-home/data/thunks';
@@ -65,15 +66,21 @@ function Unit({
const [iframeHeight, setIframeHeight] = useState(0);
const [hasLoaded, setHasLoaded] = useState(false);
const [discussionPosted, setDiscussionPosted] = useState(false);
const [modalOptions, setModalOptions] = useState({ open: false });
const unit = useModel('units', id);
const course = useModel('courses', courseId);
const {
celebrations: {
firstDiscussion,
firstDiscussionUserBucket,
},
contentTypeGatingEnabled,
} = course;
const dispatch = useDispatch();
const shouldCelebrateDiscussionPost = shouldCelebrateOnDiscussionPost(firstDiscussion, firstDiscussionUserBucket);
// Do not remove this hook. See function description.
useLoadBearingHook(id);
@@ -109,6 +116,9 @@ function Unit({
return () => global.removeEventListener('message', messageEventListenerRef.current);
}, [id, setIframeHeight, hasLoaded, iframeHeight, setHasLoaded, onLoaded]);
console.log('shouldCelebrateDiscussionPost:', shouldCelebrateDiscussionPost);
console.log('discussionPosted:', discussionPosted);
return (
<div className="unit">
<h2 className="mb-0 h4">{unit.title}</h2>
@@ -147,17 +157,21 @@ function Unit({
allow="microphone *; camera *; midi *; geolocation *; encrypted-media *"
frameBorder="0"
src={modalOptions.url}
style={{
width: '100%',
height: '100%',
}}
/>
)}
</>
)}
title={modalOptions.title}
onClose={() => { setModalOptions({ open: false }); }}
open
dialogClassName="modal-lti"
dialogClassName="modal-lg"
/>
)}
{discussionPosted && shouldCelebrateDiscussionPost && (
<FirstDiscussionCelebrationModal
courseId={courseId}
firstDiscussionUserBucket={firstDiscussionUserBucket}
open
/>
)}
<div className="unit-iframe-wrapper">
@@ -170,9 +184,11 @@ function Unit({
scrolling="no"
referrerPolicy="origin"
onLoad={() => {
window.onmessage = function handleResetDates(e) {
if (e.data.event_name) {
window.onmessage = function handleMessageEvent(e) {
if (e.data.event_name === 'post_event') {
dispatch(processEvent(e.data, fetchCourse));
} else if (e.data.event_name === 'discussion_post') {
setDiscussionPosted(true);
}
};
}}

View File

@@ -8,7 +8,7 @@ const messages = defineMessages({
},
'learn.contentLock.complete.prerequisite': {
id: 'learn.contentLock.complete.prerequisite',
defaultMessage: "You must complete the prerequisite: '{prereqSectionName}' to access this content.",
defaultMessage: 'You must complete the prerequisite: "{prereqSectionName}" to access this content.',
description: 'Message shown to indicate which prerequisite the student must complete prior to accessing the locked content. {prereqSectionName} is the name of the prerequisite.',
},
'learn.contentLock.goToSection': {

View File

@@ -54,9 +54,7 @@ function SocialIcons({
});
};
const socialUtmCampaign = getConfig().SOCIAL_UTM_MILESTONE_CAMPAIGN
? `utm_campaign=${getConfig().SOCIAL_UTM_MILESTONE_CAMPAIGN}&` : '';
const socialUtmMarketingUrl = `${marketingUrl}?${socialUtmCampaign}utm_medium=social`;
const socialUtmMarketingUrl = `${marketingUrl}?utm_campaign=edxmilestone&utm_medium=social`;
return (
<div className={`social-icons ${className}`}>
@@ -93,7 +91,7 @@ function SocialIcons({
body={emailBody ? `${intl.formatMessage(emailBody)}\n\n` : ''}
className="ml-2"
subject={emailSubject ? intl.formatMessage(emailSubject, { platform: getConfig().SITE_NAME, title }) : ''}
url={`${marketingUrl}?${socialUtmCampaign}utm_medium=email&utm_source=email`}
url={`${marketingUrl}?utm_campaign=edxmilestone&utm_medium=email&utm_source=email`}
>
<EmailIcon round size={32} />
<span className="sr-only">{intl.formatMessage(messages.shareEmail)}</span>
@@ -107,7 +105,7 @@ SocialIcons.defaultProps = {
className: '',
emailBody: messages.defaultEmailBody,
emailSubject: null,
hashtags: [getConfig().TWITTER_HASHTAG],
hashtags: ['myedxjourney'],
socialMessage: null,
};

View File

@@ -81,13 +81,8 @@ initialize({
config: () => {
mergeConfig({
INSIGHTS_BASE_URL: process.env.INSIGHTS_BASE_URL || null,
SOCIAL_UTM_MILESTONE_CAMPAIGN: process.env.SOCIAL_UTM_MILESTONE_CAMPAIGN || null,
STUDIO_BASE_URL: process.env.STUDIO_BASE_URL || null,
SUPPORT_URL: process.env.SUPPORT_URL || null,
SUPPORT_URL_CALCULATOR_MATH: process.env.SUPPORT_URL_CALCULATOR_MATH || null,
SUPPORT_URL_ID_VERIFICATION: process.env.SUPPORT_URL_ID_VERIFICATION || null,
SUPPORT_URL_VERIFIED_CERTIFICATE: process.env.SUPPORT_URL_VERIFIED_CERTIFICATE || null,
TWITTER_HASHTAG: process.env.TWITTER_HASHTAG || null,
TWITTER_URL: process.env.TWITTER_URL || null,
ENTERPRISE_LEARNER_PORTAL_HOSTNAME: process.env.ENTERPRISE_LEARNER_PORTAL_HOSTNAME || null,
}, 'LearnerAppConfig');

View File

@@ -319,27 +319,6 @@ $primary: #1176B2;
}
}
// This class forces any modals using 'modal-lti' as their dialogClassName to take up the whole
// window (retaining padding around the edge). Bootstrap modals don't have a full-screen
// size like this. Because of the hack below around react-focus-on's div, it would be better long-term to pull this into Paragon and perhaps call it "modal-full" or something like that.
.modal-lti {
height: 100%;
max-width: 100%;
// I don't like this. We need to set a height of 100% on a div created by react-focus-on, a
// package we use in our Modal. That div has no class name or ID, so instead we're uniquely
// identifying it by based on a unique attribute it has which its siblings don't share.
> div[data-focus-lock-disabled=false] {
height: 100%;
}
// Along with setting the height of modal-content's parent div from react-focus-on, we need to
// set modal-content's height as well to get the modal to expand to full-screen height.
.modal-content {
height: 100%;
}
}
// Import component-specific sass files
@import 'courseware/course/celebration/CelebrationModal.scss';
@import 'courseware/course/content-tools/calculator/calculator.scss';