feat(client): redirect from /settings + /update-email after sign out (#64405)

This commit is contained in:
Oliver Eyton-Williams
2025-12-10 12:13:41 +01:00
committed by GitHub
parent c0b9aa2cb0
commit a67abd3fbc
2 changed files with 55 additions and 5 deletions

View File

@@ -5,7 +5,7 @@ import { connect } from 'react-redux';
import { useTranslation } from 'react-i18next';
import { Button, Modal, Spacer } from '@freecodecamp/ui';
import { hardGoTo as navigate, closeSignoutModal } from '../../redux/actions';
import { closeSignoutModal } from '../../redux/actions';
import { isSignoutModalOpenSelector } from '../../redux/selectors';
import { apiLocation } from '../../../config/env.json';
import callGA from '../../analytics/call-ga';
@@ -20,20 +20,31 @@ const mapStateToProps = createSelector(
const mapDispatchToProps = (dispatch: Dispatch<AnyAction>) =>
bindActionCreators(
{
navigate,
closeSignoutModal
},
dispatch
);
type SignoutModalProps = {
navigate: (path: string) => void;
closeSignoutModal: () => void;
show: boolean;
};
export const pathAfterSignout = (currentPath: string): string => {
// These pages try to sign in the user automatically if they are signed out,
// so, to respect the user's intention to sign out, we redirect them to /learn
// instead.
const redirectedPaths = ['/settings', '/update-email'];
const allPaths = [
...redirectedPaths,
...redirectedPaths.map(path => `${path}/`)
];
return allPaths.some(path => currentPath === path) ? '/learn' : currentPath;
};
function SignoutModal(props: SignoutModalProps): JSX.Element {
const { show, closeSignoutModal, navigate } = props;
const { show, closeSignoutModal } = props;
const { t } = useTranslation();
const handleModalHide = () => {
@@ -43,7 +54,15 @@ function SignoutModal(props: SignoutModalProps): JSX.Element {
const handleSignout = () => {
closeSignoutModal();
callGA({ event: 'sign_out', user_id: undefined });
navigate(`${apiLocation}/signout`);
const redirect = () => {
window.location.pathname = pathAfterSignout(window.location.pathname);
};
void fetch(`${apiLocation}/signout`, {
method: 'GET',
credentials: 'include'
})
.then(redirect)
.catch(redirect);
};
return (

View File

@@ -0,0 +1,31 @@
import { describe, it, expect } from 'vitest';
import { pathAfterSignout } from '.';
describe('pathAfterSignout', () => {
it('should default to the supplied path', () => {
const unexceptionalPath = 'a/normal/path/';
expect(pathAfterSignout(unexceptionalPath)).toBe(unexceptionalPath);
});
it('should redirect paths that automatically sign in back to /learn', () => {
const pathsThatAttemptToSignIn = ['/settings', '/update-email'];
const newPaths = pathsThatAttemptToSignIn.map(pathAfterSignout);
expect(newPaths).toEqual(['/learn', '/learn']);
});
it('should redirect paths with trailing slashes', () => {
const pathsThatAttemptToSignIn = ['/settings/', '/update-email/'];
const newPaths = pathsThatAttemptToSignIn.map(pathAfterSignout);
expect(newPaths).toEqual(['/learn', '/learn']);
});
it('should only redirect exact matches', () => {
const similarPaths = ['/settingss', '/update-emails/', '/settings/2'];
const newPaths = similarPaths.map(pathAfterSignout);
expect(newPaths).toEqual(similarPaths);
});
});