diff --git a/package.json b/package.json index b08115d..3885a6d 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,7 @@ "formdata-polyfill": "4.0.10", "history": "4.10.1", "jslib-html5-camera-photo": "3.1.8", + "lodash.camelcase": "^4.3.0", "lodash.debounce": "4.0.8", "lodash.findindex": "4.6.0", "lodash.get": "4.4.2", diff --git a/src/account-settings/name-change/NameChange.jsx b/src/account-settings/name-change/NameChange.jsx index 707b9d0..3a99b66 100644 --- a/src/account-settings/name-change/NameChange.jsx +++ b/src/account-settings/name-change/NameChange.jsx @@ -69,7 +69,7 @@ function NameChangeModal({ useEffect(() => { if (saveState === 'complete') { handleClose(); - push('/id-verification'); + push(`/id-verification?next=${encodeURIComponent('account/settings')}`); } }, [saveState]); diff --git a/src/hooks.js b/src/hooks.js index aed3fd9..69dd638 100644 --- a/src/hooks.js +++ b/src/hooks.js @@ -29,3 +29,27 @@ export function useAsyncCall(asyncFunc) { return data; } + +// Redirect the user to their original location based on session storage +export function useRedirect() { + const [redirect, setRedirect] = useState({ + location: 'dashboard', + text: 'id.verification.return.dashboard', + }); + + useEffect(() => { + if (sessionStorage.getItem('courseId')) { + setRedirect({ + location: `courses/${sessionStorage.getItem('courseId')}`, + text: 'id.verification.return.course', + }); + } else if (sessionStorage.getItem('next')) { + setRedirect({ + location: sessionStorage.getItem('next'), + text: 'id.verification.return.generic', + }); + } + }, []); + + return redirect; +} diff --git a/src/id-verification/IdVerification.messages.js b/src/id-verification/IdVerification.messages.js index a7f0879..39a2ec0 100644 --- a/src/id-verification/IdVerification.messages.js +++ b/src/id-verification/IdVerification.messages.js @@ -701,6 +701,11 @@ const messages = defineMessages({ defaultMessage: 'Return to Course', description: 'Return to the course which ID verification was accessed from.', }, + 'id.verification.return.generic': { + id: 'id.verification.return.generic', + defaultMessage: 'Return', + description: 'Button to return to the user\'s original location.', + }, 'id.verification.photo.upload.help.title': { id: 'id.verification.photo.upload.help.title', defaultMessage: 'Upload a Photo Instead', diff --git a/src/id-verification/IdVerificationPage.jsx b/src/id-verification/IdVerificationPage.jsx index 97fdbd7..a8a7944 100644 --- a/src/id-verification/IdVerificationPage.jsx +++ b/src/id-verification/IdVerificationPage.jsx @@ -3,6 +3,7 @@ import { connect } from 'react-redux'; import { Route, Switch, Redirect, useRouteMatch, useLocation, } from 'react-router-dom'; +import camelCase from 'lodash.camelcase'; import qs from 'qs'; import { injectIntl, intlShape } from '@edx/frontend-platform/i18n'; import { Modal, Button } from '@edx/paragon'; @@ -32,16 +33,16 @@ function IdVerificationPage(props) { const [isModalOpen, setIsModalOpen] = useState(false); - // Course run key is passed as a query string + // Save query params in order to route back to the correct location later useEffect(() => { if (search) { - const parsed = qs.parse(search, { + const parsedQueryParams = qs.parse(search, { ignoreQueryPrefix: true, interpretNumericEntities: true, }); - if (Object.prototype.hasOwnProperty.call(parsed, 'course_id') && parsed.course_id) { - sessionStorage.setItem('courseRunKey', parsed.course_id); - } + Object.entries(parsedQueryParams).forEach(([key, value]) => { + sessionStorage.setItem(camelCase(key), value); + }); } }, [search]); diff --git a/src/id-verification/panels/RequestCameraAccessPanel.jsx b/src/id-verification/panels/RequestCameraAccessPanel.jsx index 57728d2..739902a 100644 --- a/src/id-verification/panels/RequestCameraAccessPanel.jsx +++ b/src/id-verification/panels/RequestCameraAccessPanel.jsx @@ -1,10 +1,11 @@ -import React, { useState, useEffect, useContext } from 'react'; +import React, { useEffect, useContext } from 'react'; import { Link } from 'react-router-dom'; import Bowser from 'bowser'; import { getConfig } from '@edx/frontend-platform'; import { sendTrackEvent } from '@edx/frontend-platform/analytics'; import { injectIntl, intlShape, FormattedMessage } from '@edx/frontend-platform/i18n'; +import { useRedirect } from '../../hooks'; import { useNextPanelSlug } from '../routing-utilities'; import BasePanel from './BasePanel'; import IdVerificationContext, { MEDIA_ACCESS } from '../IdVerificationContext'; @@ -14,8 +15,7 @@ import { UnsupportedCameraDirectionsPanel } from './UnsupportedCameraDirectionsP import messages from '../IdVerification.messages'; function RequestCameraAccessPanel(props) { - const [returnUrl, setReturnUrl] = useState('dashboard'); - const [returnText, setReturnText] = useState('id.verification.return.dashboard'); + const { location: returnUrl, text: returnText } = useRedirect(); const panelSlug = 'request-camera-access'; const nextPanelSlug = useNextPanelSlug(panelSlug); const { @@ -38,15 +38,6 @@ function RequestCameraAccessPanel(props) { } }, [mediaAccess, userId]); - // If the user accessed IDV through a course, - // link back to that course rather than the dashboard - useEffect(() => { - if (sessionStorage.getItem('courseRunKey')) { - setReturnUrl(`courses/${sessionStorage.getItem('courseRunKey')}`); - setReturnText('id.verification.return.course'); - } - }, []); - const getTitle = () => { if (mediaAccess === MEDIA_ACCESS.GRANTED) { return props.intl.formatMessage(messages['id.verification.camera.access.title.success']); @@ -57,7 +48,7 @@ function RequestCameraAccessPanel(props) { return props.intl.formatMessage(messages['id.verification.camera.access.title']); }; - const returnToDashboardLink = ( + const returnLink = ( {props.intl.formatMessage(messages[returnText])} @@ -114,7 +105,7 @@ function RequestCameraAccessPanel(props) {