AA-287 cta events (#177)

This commit is contained in:
Nick
2020-08-12 11:48:46 -04:00
committed by GitHub
parent a131a9f9fb
commit 453f56c7c8
9 changed files with 73 additions and 50 deletions

View File

@@ -5,7 +5,8 @@ Object {
"courseHome": Object {
"courseId": null,
"courseStatus": "loading",
"displayResetDatesToast": false,
"resetDatesToastBody": null,
"resetDatesToastHeader": null,
},
"courseware": Object {
"courseId": null,
@@ -22,7 +23,8 @@ Object {
"courseHome": Object {
"courseId": "course-v1:edX+DemoX+Demo_Course_1",
"courseStatus": "loaded",
"displayResetDatesToast": false,
"resetDatesToastBody": null,
"resetDatesToastHeader": null,
},
"courseware": Object {
"courseId": null,
@@ -106,7 +108,8 @@ Object {
"courseHome": Object {
"courseId": "course-v1:edX+DemoX+Demo_Course_1",
"courseStatus": "loaded",
"displayResetDatesToast": false,
"resetDatesToastBody": null,
"resetDatesToastHeader": null,
},
"courseware": Object {
"courseId": null,

View File

@@ -93,7 +93,7 @@ export async function getOutlineTabData(courseId) {
export async function postCourseDeadlines(courseId) {
const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_experience/v1/reset_course_deadlines`);
await getAuthenticatedHttpClient().post(url.href, { course_key: courseId });
return getAuthenticatedHttpClient().post(url.href, { course_key: courseId });
}
export async function postDismissWelcomeMessage(courseId) {
@@ -105,3 +105,8 @@ export async function postRequestCert(courseId) {
const url = new URL(`${getConfig().LMS_BASE_URL}/courses/${courseId}/generate_user_cert`);
await getAuthenticatedHttpClient().post(url.href);
}
export async function executePostFromPostEvent(postData) {
const url = new URL(postData.url);
return getAuthenticatedHttpClient().post(url.href, { course_key: postData.bodyParams.courseId });
}

View File

@@ -109,7 +109,7 @@ describe('Data layer integration tests', () => {
describe('Test resetDeadlines', () => {
it('Should reset course deadlines', async () => {
const resetUrl = `${getConfig().LMS_BASE_URL}/api/course_experience/v1/reset_course_deadlines`;
axiosMock.onPost(resetUrl).reply(201);
axiosMock.onPost(resetUrl).reply(201, {});
const getTabDataMock = jest.fn(() => ({
type: 'MOCK_ACTION',

View File

@@ -10,7 +10,8 @@ const slice = createSlice({
initialState: {
courseStatus: 'loading',
courseId: null,
displayResetDatesToast: false,
resetDatesToastBody: null,
resetDatesToastHeader: null,
},
reducers: {
fetchTabRequest: (state, { payload }) => {
@@ -25,8 +26,13 @@ const slice = createSlice({
state.courseId = payload.courseId;
state.courseStatus = FAILED;
},
toggleResetDatesToast: (state, { payload }) => {
state.displayResetDatesToast = payload.displayResetDatesToast;
setResetDatesToast: (state, { payload }) => {
const {
body,
header,
} = payload;
state.resetDatesToastBody = body;
state.resetDatesToastHeader = header;
},
},
});
@@ -35,7 +41,7 @@ export const {
fetchTabRequest,
fetchTabSuccess,
fetchTabFailure,
toggleResetDatesToast,
setResetDatesToast,
} = slice.actions;
export const {

View File

@@ -1,5 +1,7 @@
import { logError } from '@edx/frontend-platform/logging';
import { camelCaseObject } from '@edx/frontend-platform';
import {
executePostFromPostEvent,
getCourseHomeCourseMetadata,
getDatesTabData,
getOutlineTabData,
@@ -17,9 +19,13 @@ import {
fetchTabFailure,
fetchTabRequest,
fetchTabSuccess,
toggleResetDatesToast,
setResetDatesToast,
} from './slice';
const eventTypes = {
POST_EVENT: 'post_event',
};
export function fetchTab(courseId, tab, getTabData) {
return async (dispatch) => {
dispatch(fetchTabRequest({ courseId }));
@@ -77,9 +83,14 @@ export function fetchOutlineTab(courseId) {
export function resetDeadlines(courseId, getTabData) {
return async (dispatch) => {
postCourseDeadlines(courseId).then(() => {
postCourseDeadlines(courseId).then(response => {
const { data } = response;
const {
body,
header,
} = data;
dispatch(getTabData(courseId));
dispatch(toggleResetDatesToast({ displayResetDatesToast: true }));
dispatch(setResetDatesToast({ body, header }));
});
};
}
@@ -91,3 +102,20 @@ export function dismissWelcomeMessage(courseId) {
export function requestCert(courseId) {
return async () => postRequestCert(courseId);
}
export function processEvent(eventData, getTabData) {
return async (dispatch) => {
const event = camelCaseObject(eventData);
if (event.eventName === eventTypes.POST_EVENT) {
executePostFromPostEvent(event.postData).then(response => {
const { data } = response;
const {
body,
header,
} = data;
dispatch(getTabData(event.postData.bodyParams.courseId));
dispatch(setResetDatesToast({ body, header }));
});
}
};
}

View File

@@ -13,7 +13,7 @@ import messages from './messages';
import BookmarkButton from '../bookmark/BookmarkButton';
import { useModel } from '../../../generic/model-store';
import PageLoading from '../../../generic/PageLoading';
import { resetDeadlines } from '../../../course-home/data/thunks';
import { processEvent } from '../../../course-home/data/thunks';
import { fetchCourse } from '../../data/thunks';
const LockPaywall = React.lazy(() => import('./lock-paywall'));
@@ -138,8 +138,8 @@ function Unit({
referrerPolicy="origin"
onLoad={() => {
window.onmessage = function (e) {
if (e.data === 'reset_dates') {
dispatch(resetDeadlines(courseId, fetchCourse));
if (e.data.event_name) {
dispatch(processEvent(e.data, fetchCourse));
}
};
}}

View File

@@ -9,16 +9,16 @@ import PageLoading from '../generic/PageLoading';
import messages from './messages';
import LoadedTabPage from './LoadedTabPage';
import LearningToast from '../toast/LearningToast';
import { toggleResetDatesToast } from '../course-home/data/slice';
import { setResetDatesToast } from '../course-home/data/slice';
function TabPage({
intl,
courseStatus,
...passthroughProps
}) {
const courseId = useSelector(state => state.courseHome.courseId || state.courseware.courseId);
const {
displayResetDatesToast,
resetDatesToastBody,
resetDatesToastHeader,
} = useSelector(state => state.courseHome);
const dispatch = useDispatch();
@@ -37,11 +37,10 @@ function TabPage({
return (
<>
<LearningToast
body={messages.datesResetSuccessBody}
header={messages.datesResetSuccessHeader}
link={`/course/${courseId}/dates`}
onClose={() => dispatch(toggleResetDatesToast({ displayResetDatesToast: false }))}
show={displayResetDatesToast}
body={resetDatesToastBody}
header={resetDatesToastHeader}
onClose={() => dispatch(setResetDatesToast({ body: null, header: null }))}
show={!!(resetDatesToastBody && resetDatesToastHeader)}
/>
<LoadedTabPage {...passthroughProps} />
</>

View File

@@ -9,14 +9,6 @@ const messages = defineMessages({
id: 'learning.loading',
defaultMessage: 'Loading course page…',
},
datesResetSuccessBody: {
id: 'learning.datesResetSuccessBody',
defaultMessage: 'View all dates',
},
datesResetSuccessHeader: {
id: 'learning.datesResetSuccessHeader',
defaultMessage: 'Your due dates have been successfully shifted to help you stay on track.',
},
});
export default messages;

View File

@@ -1,16 +1,12 @@
import React from 'react';
import PropTypes from 'prop-types';
import { injectIntl, intlShape } from '@edx/frontend-platform/i18n';
import { Toast } from '@edx/paragon';
import { Link } from 'react-router-dom';
import './LearningToast.scss';
function LearningToast({
intl,
export default function LearningToast({
body,
header,
link,
onClose,
show,
}) {
@@ -29,29 +25,23 @@ function LearningToast({
}}
>
<Toast.Header className="bg-gray-700 border-bottom-0 text-light">
<div className="mr-auto">{intl.formatMessage(header)}</div>
<div dangerouslySetInnerHTML={{ __html: header }} />
</Toast.Header>
<Toast.Body className="bg-gray-700 text-light">
<Link className="text-light" to={link}>{intl.formatMessage(body)}</Link>
<div dangerouslySetInnerHTML={{ __html: body }} />
</Toast.Body>
</Toast>
);
}
LearningToast.propTypes = {
body: PropTypes.shape({
possible: PropTypes.number,
earned: PropTypes.number,
graded: PropTypes.bool,
}).isRequired,
header: PropTypes.shape({
id: PropTypes.string,
defaultMessage: PropTypes.string,
}).isRequired,
intl: intlShape.isRequired,
link: PropTypes.string.isRequired,
body: PropTypes.string,
header: PropTypes.string,
onClose: PropTypes.func.isRequired,
show: PropTypes.bool.isRequired,
};
export default injectIntl(LearningToast);
LearningToast.defaultProps = {
body: null,
header: null,
};