Skip to content

Commit 3e1da8f

Browse files
refactor(client): store session user in dedicated key (freeCodeCamp#59954)
1 parent df32414 commit 3e1da8f

30 files changed

+412
-526
lines changed

client/src/client-only-routes/show-certification.tsx

Lines changed: 28 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@ import {
2121
showCertFetchStateSelector,
2222
userFetchStateSelector,
2323
isDonatingSelector,
24-
userByNameSelector,
25-
usernameSelector
24+
usernameSelector,
25+
createUserByNameSelector,
26+
isSignedInSelector
2627
} from '../redux/selectors';
27-
import { UserFetchState, User } from '../redux/prop-types';
28+
import type { UserFetchState, User } from '../redux/prop-types';
2829
import { liveCerts } from '../../config/cert-and-project-map';
2930
import {
3031
certificateMissingErrorMessage,
@@ -67,6 +68,7 @@ interface ShowCertificationProps {
6768
};
6869
isDonating: boolean;
6970
isValidCert: boolean;
71+
isSignedIn: boolean;
7072
location: {
7173
pathname: string;
7274
};
@@ -78,41 +80,48 @@ interface ShowCertificationProps {
7880
certSlug: string;
7981
}) => void;
8082
signedInUserName: string;
81-
user: User;
83+
user: User | null;
8284
userFetchState: UserFetchState;
8385
userFullName: string;
8486
username: string;
8587
}
8688

87-
const requestedUserSelector = (state: unknown, { username = '' }) =>
88-
userByNameSelector(username.toLowerCase())(state) as User;
89-
9089
const mapStateToProps = (state: unknown, props: ShowCertificationProps) => {
9190
const isValidCert = liveCerts.some(
9291
({ certSlug }) => String(certSlug) === props.certSlug
9392
);
93+
94+
const { username } = props;
95+
96+
const userByNameSelector = createUserByNameSelector(username) as (
97+
state: unknown
98+
) => User | null;
99+
94100
return createSelector(
95101
showCertSelector,
96102
showCertFetchStateSelector,
97103
usernameSelector,
104+
userByNameSelector,
98105
userFetchStateSelector,
99106
isDonatingSelector,
100-
requestedUserSelector,
107+
isSignedInSelector,
101108
(
102109
cert: Cert,
103110
fetchState: ShowCertificationProps['fetchState'],
104111
signedInUserName: string,
112+
user: User | null,
105113
userFetchState: UserFetchState,
106114
isDonating: boolean,
107-
user: User
115+
isSignedIn: boolean
108116
) => ({
109117
cert,
110118
fetchState,
111119
isValidCert,
112120
signedInUserName,
121+
user,
113122
userFetchState,
114123
isDonating,
115-
user
124+
isSignedIn
116125
})
117126
);
118127
};
@@ -293,24 +302,19 @@ const ShowCertification = (props: ShowCertificationProps): JSX.Element => {
293302
userFetchState: { complete: userComplete },
294303
signedInUserName,
295304
isDonating,
305+
isSignedIn,
296306
cert: { username = '' },
297307
fetchProfileForUser,
298308
user
299309
} = props;
300310

301-
if (!signedInUserName || signedInUserName !== username) {
302-
if (isEmpty(user) && username) {
303-
fetchProfileForUser(username);
304-
}
311+
const isSessionUser = isSignedIn && signedInUserName === username;
312+
313+
if (isEmpty(user) && username) {
314+
fetchProfileForUser(username);
305315
}
306316

307-
if (
308-
!isDonationDisplayed &&
309-
userComplete &&
310-
signedInUserName &&
311-
signedInUserName === username &&
312-
!isDonating
313-
) {
317+
if (!isDonationDisplayed && userComplete && isSessionUser && !isDonating) {
314318
setIsDonationDisplayed(true);
315319
callGA({
316320
event: 'donation_view',
@@ -341,7 +345,8 @@ const ShowCertification = (props: ShowCertificationProps): JSX.Element => {
341345
isValidCert,
342346
createFlashMessage,
343347
signedInUserName,
344-
location: { pathname }
348+
location: { pathname },
349+
user
345350
} = props;
346351
const { pending, complete, errored } = fetchState;
347352

@@ -359,7 +364,7 @@ const ShowCertification = (props: ShowCertificationProps): JSX.Element => {
359364
return <RedirectHome />;
360365
}
361366

362-
if (pending) {
367+
if (pending || !user) {
363368
return <Loader fullScreen={true} />;
364369
}
365370

@@ -376,8 +381,6 @@ const ShowCertification = (props: ShowCertificationProps): JSX.Element => {
376381
completionTime
377382
} = cert;
378383

379-
const { user } = props;
380-
381384
const displayName = userFullName ?? username;
382385

383386
const certDate = new Date(date);

client/src/client-only-routes/show-profile-or-four-oh-four.tsx

Lines changed: 23 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -8,50 +8,43 @@ import Loader from '../components/helpers/loader';
88
import Profile from '../components/profile/profile';
99
import { fetchProfileForUser } from '../redux/actions';
1010
import {
11-
usernameSelector,
12-
userByNameSelector,
13-
userProfileFetchStateSelector
11+
userSelector,
12+
userProfileFetchStateSelector,
13+
createUserByNameSelector
1414
} from '../redux/selectors';
15-
import { User } from '../redux/prop-types';
15+
import type { User } from '../redux/prop-types';
1616
import { Socials } from '../components/profile/components/internet';
1717

1818
interface ShowProfileOrFourOhFourProps {
1919
fetchProfileForUser: (username: string) => void;
2020
updateMyPortfolio: () => void;
2121
submitNewAbout: () => void;
2222
updateMySocials: (formValues: Socials) => void;
23-
fetchState: {
24-
pending: boolean;
25-
complete: boolean;
26-
errored: boolean;
27-
};
2823
isSessionUser: boolean;
2924
maybeUser?: string;
30-
requestedUser: User;
25+
requestedUser: User | null;
3126
showLoading: boolean;
3227
}
3328

34-
const createRequestedUserSelector =
35-
() =>
36-
(state: unknown, { maybeUser = '' }) =>
37-
userByNameSelector(maybeUser.toLowerCase())(state) as User;
38-
const createIsSessionUserSelector =
29+
const makeMapStateToProps =
3930
() =>
40-
(state: unknown, { maybeUser = '' }) =>
41-
maybeUser.toLowerCase() === usernameSelector(state);
31+
(state: unknown, { maybeUser = '' }) => {
32+
const username = maybeUser.toLowerCase();
33+
const requestedUser = (
34+
createUserByNameSelector as (
35+
maybeUser: string
36+
) => (state: unknown) => User | null
37+
)(username)(state);
38+
const sessionUser = userSelector(state) as User | null;
39+
const isSessionUser = username === sessionUser?.username;
40+
const fetchState = userProfileFetchStateSelector(state) as {
41+
pending: boolean;
42+
};
4243

43-
const makeMapStateToProps =
44-
() => (state: unknown, props: ShowProfileOrFourOhFourProps) => {
45-
const requestedUserSelector = createRequestedUserSelector();
46-
const isSessionUserSelector = createIsSessionUserSelector();
47-
const fetchState = userProfileFetchStateSelector(
48-
state
49-
) as ShowProfileOrFourOhFourProps['fetchState'];
5044
return {
51-
requestedUser: requestedUserSelector(state, props),
52-
isSessionUser: isSessionUserSelector(state, props),
53-
showLoading: fetchState.pending,
54-
fetchState
45+
requestedUser,
46+
isSessionUser,
47+
showLoading: fetchState.pending
5548
};
5649
};
5750

@@ -70,10 +63,8 @@ function ShowProfileOrFourOhFour({
7063
}: ShowProfileOrFourOhFourProps) {
7164
useEffect(() => {
7265
// If the user is not already in the store, fetch it
73-
if (isEmpty(requestedUser)) {
74-
if (maybeUser) {
75-
fetchProfileForUser(maybeUser);
76-
}
66+
if (isEmpty(requestedUser) && maybeUser) {
67+
fetchProfileForUser(maybeUser);
7768
}
7869
// eslint-disable-next-line react-hooks/exhaustive-deps
7970
}, []);

client/src/client-only-routes/show-settings.tsx

Lines changed: 41 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ import {
2626
isSignedInSelector,
2727
userTokenSelector
2828
} from '../redux/selectors';
29-
import { User } from '../redux/prop-types';
29+
import type { User } from '../redux/prop-types';
3030
import {
3131
submitNewAbout,
3232
updateMyHonesty,
@@ -50,7 +50,7 @@ type ShowSettingsProps = {
5050
toggleKeyboardShortcuts: (keyboardShortcuts: boolean) => void;
5151
updateIsHonest: () => void;
5252
updateQuincyEmail: (isSendQuincyEmail: boolean) => void;
53-
user: User;
53+
user: User | null;
5454
verifyCert: typeof verifyCert;
5555
path?: string;
5656
userToken: string | null;
@@ -61,7 +61,12 @@ const mapStateToProps = createSelector(
6161
userSelector,
6262
isSignedInSelector,
6363
userTokenSelector,
64-
(showLoading: boolean, user: User, isSignedIn, userToken: string | null) => ({
64+
(
65+
showLoading: boolean,
66+
user: User | null,
67+
isSignedIn,
68+
userToken: string | null
69+
) => ({
6570
showLoading,
6671
user,
6772
isSignedIn,
@@ -91,53 +96,57 @@ export function ShowSettings(props: ShowSettingsProps): JSX.Element {
9196
toggleSoundMode,
9297
toggleKeyboardShortcuts,
9398
resetEditorLayout,
94-
user: {
95-
completedChallenges,
96-
email,
97-
is2018DataVisCert,
98-
isApisMicroservicesCert,
99-
isJsAlgoDataStructCert,
100-
isBackEndCert,
101-
isDataVisCert,
102-
isFrontEndCert,
103-
isInfosecQaCert,
104-
isQaCertV7,
105-
isInfosecCertV7,
106-
isFrontEndLibsCert,
107-
isFullStackCert,
108-
isRespWebDesignCert,
109-
isSciCompPyCertV7,
110-
isDataAnalysisPyCertV7,
111-
isMachineLearningPyCertV7,
112-
isRelationalDatabaseCertV8,
113-
isCollegeAlgebraPyCertV8,
114-
isFoundationalCSharpCertV8,
115-
isJsAlgoDataStructCertV8,
116-
isEmailVerified,
117-
isHonest,
118-
sendQuincyEmail,
119-
username,
120-
keyboardShortcuts
121-
},
99+
user,
122100
navigate,
123101
showLoading,
124102
updateQuincyEmail,
125103
updateIsHonest,
126104
verifyCert,
127105
userToken
128106
} = props;
107+
129108
const isSignedInRef = useRef(isSignedIn);
130109

131110
const examTokenFlag = useFeatureIsOn('exam-token-widget');
132111

133-
if (showLoading) {
112+
if (showLoading || !user) {
134113
return <Loader fullScreen={true} />;
135114
}
136115

137116
if (!isSignedInRef.current) {
138117
navigate(`${apiLocation}/signin`);
139118
return <Loader fullScreen={true} />;
140119
}
120+
121+
const {
122+
completedChallenges,
123+
email,
124+
is2018DataVisCert,
125+
isApisMicroservicesCert,
126+
isJsAlgoDataStructCert,
127+
isBackEndCert,
128+
isDataVisCert,
129+
isFrontEndCert,
130+
isInfosecQaCert,
131+
isQaCertV7,
132+
isInfosecCertV7,
133+
isFrontEndLibsCert,
134+
isFullStackCert,
135+
isRespWebDesignCert,
136+
isSciCompPyCertV7,
137+
isDataAnalysisPyCertV7,
138+
isMachineLearningPyCertV7,
139+
isRelationalDatabaseCertV8,
140+
isCollegeAlgebraPyCertV8,
141+
isFoundationalCSharpCertV8,
142+
isJsAlgoDataStructCertV8,
143+
isEmailVerified,
144+
isHonest,
145+
sendQuincyEmail,
146+
username,
147+
keyboardShortcuts
148+
} = user;
149+
141150
const sound = (store.get('fcc-sound') as boolean) ?? false;
142151
const editorLayout = (store.get('challenge-layout') as boolean) ?? false;
143152
return (

client/src/client-only-routes/show-update-email.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import { isSignedInSelector, userSelector } from '../redux/selectors';
2727
import { hardGoTo as navigate } from '../redux/actions';
2828
import { updateMyEmail } from '../redux/settings/actions';
2929
import { maybeEmailRE } from '../utils';
30+
import type { User } from '../redux/prop-types';
3031

3132
const { apiLocation } = envData;
3233

@@ -41,11 +42,8 @@ interface ShowUpdateEmailProps {
4142
const mapStateToProps = createSelector(
4243
userSelector,
4344
isSignedInSelector,
44-
(
45-
{ email, emailVerified }: { email: string; emailVerified: boolean },
46-
isSignedIn
47-
) => ({
48-
isNewEmail: !email || emailVerified,
45+
(user: User | null, isSignedIn) => ({
46+
isNewEmail: !user?.email || user.emailVerified,
4947
isSignedIn
5048
})
5149
);

client/src/components/Donation/donate-form.tsx

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ import {
2525
themeSelector
2626
} from '../../redux/selectors';
2727
import { LocalStorageThemes, DonateFormState } from '../../redux/types';
28-
import type { CompletedChallenge } from '../../redux/prop-types';
28+
import type { CompletedChallenge, User } from '../../redux/prop-types';
2929
import { CENTS_IN_DOLLAR, formattedAmountLabel } from './utils';
3030
import DonateCompletion from './donate-completion';
3131
import PatreonButton from './patreon-button';
@@ -62,7 +62,7 @@ type PostCharge = (data: {
6262
type DonateFormProps = {
6363
postCharge: PostCharge;
6464
defaultTheme?: LocalStorageThemes;
65-
email: string;
65+
email?: string;
6666
handleProcessing?: () => void;
6767
editAmount?: () => void;
6868
selectedDonationAmount?: DonationAmount;
@@ -91,15 +91,15 @@ const mapStateToProps = createSelector(
9191
isSignedIn: DonateFormProps['isSignedIn'],
9292
isDonating: DonateFormProps['isDonating'],
9393
donationFormState: DonateFormState,
94-
{ email }: { email: string },
94+
user: User | null,
9595
completedChallenges: CompletedChallenge[],
9696
theme: LocalStorageThemes
9797
) => ({
9898
isSignedIn,
9999
isDonating,
100100
showLoading,
101101
donationFormState,
102-
email,
102+
email: user?.email,
103103
completedChallenges,
104104
theme
105105
})

0 commit comments

Comments
 (0)