diff --git a/src/courseware/course/course-exit/CourseExit.jsx b/src/courseware/course/course-exit/CourseExit.jsx index 37b28cf1..35f8e980 100644 --- a/src/courseware/course/course-exit/CourseExit.jsx +++ b/src/courseware/course/course-exit/CourseExit.jsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect } from 'react'; import { getConfig } from '@edx/frontend-platform'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; @@ -11,6 +11,7 @@ import CourseInProgress from './CourseInProgress'; import CourseNonPassing from './CourseNonPassing'; import { COURSE_EXIT_MODES, getCourseExitMode } from './utils'; import messages from './messages'; +import { unsubscribeFromGoalReminders } from './data/thunks'; import { useModel } from '../../../generic/model-store'; @@ -18,6 +19,7 @@ function CourseExit({ intl }) { const { courseId } = useSelector(state => state.courseware); const { certificateData, + enrollmentMode, hasScheduledContent, isEnrolled, userHasPassingGrade, @@ -32,6 +34,15 @@ function CourseExit({ intl }) { courseExitPageIsActive, ); + // Audit users cannot fully complete a course, so we will + // unsubscribe them from goal reminders once they reach the course exit page + // to avoid spamming them with goal reminder emails + if (enrollmentMode === 'audit') { + useEffect(() => { + unsubscribeFromGoalReminders(courseId); + }, []); + } + let body = null; if (mode === COURSE_EXIT_MODES.nonPassing) { body = (); diff --git a/src/courseware/course/course-exit/CourseExit.test.jsx b/src/courseware/course/course-exit/CourseExit.test.jsx index 0a1e2c2e..1f3f6475 100644 --- a/src/courseware/course/course-exit/CourseExit.test.jsx +++ b/src/courseware/course/course-exit/CourseExit.test.jsx @@ -3,6 +3,7 @@ import MockAdapter from 'axios-mock-adapter'; import { Factory } from 'rosie'; import { getConfig } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth'; +import { waitFor } from '@testing-library/react'; import { fetchCourse } from '../../data'; import { buildSimpleCourseBlocks } from '../../../shared/data/__factories__/courseBlocks.factory'; @@ -377,4 +378,18 @@ describe('Course Exit Pages', () => { expect(screen.getByRole('link', { name: 'View course schedule' })).toBeInTheDocument(); }); }); + + it('unsubscribes the user when loading the course exit page', async () => { + setMetadata({ + enrollment: { + mode: 'audit', + }, + }); + await fetchAndRender(); + const url = `${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`; + await waitFor(() => { + expect(axiosMock.history.post[0].url).toMatch(url); + expect(axiosMock.history.post[0].data).toMatch(`{"course_id":"${defaultMetadata.id}","subscribed_to_reminders":false}`); + }); + }); }); diff --git a/src/courseware/course/course-exit/data/api.js b/src/courseware/course/course-exit/data/api.js index 000f9684..d819db72 100644 --- a/src/courseware/course/course-exit/data/api.js +++ b/src/courseware/course/course-exit/data/api.js @@ -23,7 +23,7 @@ function filterRecommendationsList( )); } -export default async function getCourseRecommendations(courseKey) { +export async function getCourseRecommendations(courseKey) { const discoveryApiUrl = getConfig().DISCOVERY_API_BASE_URL; if (!discoveryApiUrl) { return []; @@ -36,3 +36,11 @@ export default async function getCourseRecommendations(courseKey) { ]); return filterRecommendationsList(camelCaseObject(recommendationsResponse), camelCaseObject(enrollmentsResponse)); } + +export async function postUnsubscribeFromGoalReminders(courseId) { + const url = new URL(`${getConfig().LMS_BASE_URL}/api/course_home/save_course_goal`); + return getAuthenticatedHttpClient().post(url.href, { + course_id: courseId, + subscribed_to_reminders: false, + }); +} diff --git a/src/courseware/course/course-exit/data/thunks.js b/src/courseware/course/course-exit/data/thunks.js index f355afb0..56d66961 100644 --- a/src/courseware/course/course-exit/data/thunks.js +++ b/src/courseware/course/course-exit/data/thunks.js @@ -5,7 +5,10 @@ import { fetchCourseRecommendationsRequest, fetchCourseRecommendationsSuccess, } from './slice'; -import getCourseRecommendations from './api'; +import { + getCourseRecommendations, + postUnsubscribeFromGoalReminders, +} from './api'; import { updateModel } from '../../../../generic/model-store'; export default function fetchCourseRecommendations(courseKey, courseId) { @@ -27,3 +30,7 @@ export default function fetchCourseRecommendations(courseKey, courseId) { } }; } + +export async function unsubscribeFromGoalReminders(courseId, daysPerWeek, subscribedToReminders) { + return postUnsubscribeFromGoalReminders(courseId, daysPerWeek, subscribedToReminders); +}