mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-02-01 12:03:59 -05:00
feat(client): pull percentage challenge data from redux (#49308)
* feat(client): pull percentage challenge data from redux * Apply suggestions from code review Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com> * feat: update updateAllChallengesInfo name --------- Co-authored-by: Oliver Eyton-Williams <ojeytonwilliams@gmail.com> Co-authored-by: Tom <20648924+moT01@users.noreply.github.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import { TFunction, withTranslation } from 'react-i18next';
|
||||
import { connect } from 'react-redux';
|
||||
import { bindActionCreators, Dispatch } from 'redux';
|
||||
import { createSelector } from 'reselect';
|
||||
import { useStaticQuery, graphql } from 'gatsby';
|
||||
|
||||
import latoBoldURL from '../../../static/fonts/lato/Lato-Bold.woff';
|
||||
import latoLightURL from '../../../static/fonts/lato/Lato-Light.woff';
|
||||
@@ -16,7 +17,8 @@ import { isBrowser } from '../../../utils';
|
||||
import {
|
||||
fetchUser,
|
||||
onlineStatusChange,
|
||||
serverStatusChange
|
||||
serverStatusChange,
|
||||
updateAllChallengesInfo
|
||||
} from '../../redux/actions';
|
||||
import {
|
||||
isSignedInSelector,
|
||||
@@ -26,7 +28,12 @@ import {
|
||||
userFetchStateSelector
|
||||
} from '../../redux/selectors';
|
||||
|
||||
import { UserFetchState, User } from '../../redux/prop-types';
|
||||
import {
|
||||
UserFetchState,
|
||||
User,
|
||||
AllChallengeNode,
|
||||
CertificateNode
|
||||
} from '../../redux/prop-types';
|
||||
import BreadCrumb from '../../templates/Challenges/components/bread-crumb';
|
||||
import Flash from '../Flash';
|
||||
import { flashMessageSelector, removeFlashMessage } from '../Flash/redux';
|
||||
@@ -77,7 +84,8 @@ const mapDispatchToProps = (dispatch: Dispatch) =>
|
||||
fetchUser,
|
||||
removeFlashMessage,
|
||||
onlineStatusChange,
|
||||
serverStatusChange
|
||||
serverStatusChange,
|
||||
updateAllChallengesInfo
|
||||
},
|
||||
dispatch
|
||||
);
|
||||
@@ -117,10 +125,13 @@ function DefaultLayout({
|
||||
t,
|
||||
theme = Themes.Default,
|
||||
user,
|
||||
fetchUser
|
||||
fetchUser,
|
||||
updateAllChallengesInfo
|
||||
}: DefaultLayoutProps): JSX.Element {
|
||||
const { challengeEdges, certificateNodes } = useGetAllBlockIds();
|
||||
useEffect(() => {
|
||||
// componentDidMount
|
||||
updateAllChallengesInfo({ challengeEdges, certificateNodes });
|
||||
if (!isSignedIn) {
|
||||
fetchUser();
|
||||
}
|
||||
@@ -237,6 +248,50 @@ function DefaultLayout({
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: get challenge nodes directly rather than wrapped in edges
|
||||
const useGetAllBlockIds = () => {
|
||||
const {
|
||||
allChallengeNode: { edges: challengeEdges },
|
||||
allCertificateNode: { nodes: certificateNodes }
|
||||
}: {
|
||||
allChallengeNode: AllChallengeNode;
|
||||
allCertificateNode: { nodes: CertificateNode[] };
|
||||
} = useStaticQuery(graphql`
|
||||
query getBlockNode {
|
||||
allChallengeNode(
|
||||
sort: {
|
||||
fields: [
|
||||
challenge___superOrder
|
||||
challenge___order
|
||||
challenge___challengeOrder
|
||||
]
|
||||
}
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
challenge {
|
||||
block
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
allCertificateNode {
|
||||
nodes {
|
||||
challenge {
|
||||
certification
|
||||
tests {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
return { challengeEdges, certificateNodes };
|
||||
};
|
||||
|
||||
DefaultLayout.displayName = 'DefaultLayout';
|
||||
|
||||
export default connect(
|
||||
|
||||
@@ -27,6 +27,7 @@ export const actionTypes = createTypes(
|
||||
'updateDonationFormState',
|
||||
'updateUserToken',
|
||||
'postChargeProcessing',
|
||||
'updateAllChallengesInfo',
|
||||
...createAsyncTypes('fetchUser'),
|
||||
...createAsyncTypes('postCharge'),
|
||||
...createAsyncTypes('fetchProfileForUser'),
|
||||
|
||||
@@ -53,6 +53,10 @@ export const fetchUser = createAction(actionTypes.fetchUser);
|
||||
export const fetchUserComplete = createAction(actionTypes.fetchUserComplete);
|
||||
export const fetchUserError = createAction(actionTypes.fetchUserError);
|
||||
|
||||
export const updateAllChallengesInfo = createAction(
|
||||
actionTypes.updateAllChallengesInfo
|
||||
);
|
||||
|
||||
export const postCharge = createAction(actionTypes.postCharge);
|
||||
export const postChargeProcessing = createAction(
|
||||
actionTypes.postChargeProcessing
|
||||
|
||||
@@ -56,6 +56,10 @@ const initialState = {
|
||||
userFetchState: {
|
||||
...defaultFetchState
|
||||
},
|
||||
allChallengesInfo: {
|
||||
challengeEdges: [],
|
||||
certificateNodes: []
|
||||
},
|
||||
userProfileFetchState: {
|
||||
...defaultFetchState
|
||||
},
|
||||
@@ -153,7 +157,10 @@ export const reducer = handleActions(
|
||||
...state,
|
||||
donationFormState: { ...defaultDonationFormState, error: payload }
|
||||
}),
|
||||
|
||||
[actionTypes.updateAllChallengesInfo]: (state, { payload }) => ({
|
||||
...state,
|
||||
allChallengesInfo: { ...payload }
|
||||
}),
|
||||
[actionTypes.fetchUser]: state => ({
|
||||
...state,
|
||||
userFetchState: { ...defaultFetchState }
|
||||
|
||||
@@ -142,6 +142,19 @@ export type ChallengeNode = {
|
||||
};
|
||||
};
|
||||
|
||||
export type CertificateNode = {
|
||||
challenge: {
|
||||
// TODO: use enum
|
||||
certification: string;
|
||||
tests: { id: string }[];
|
||||
};
|
||||
};
|
||||
|
||||
export type AllChallengesInfo = {
|
||||
challengeEdges: { node: ChallengeNode }[];
|
||||
certificateNodes: CertificateNode[];
|
||||
};
|
||||
|
||||
export type AllChallengeNode = {
|
||||
edges: [
|
||||
{
|
||||
|
||||
@@ -202,6 +202,8 @@ export const certificatesByNameSelector = username => state => {
|
||||
};
|
||||
|
||||
export const userFetchStateSelector = state => state[MainApp].userFetchState;
|
||||
export const allChallengesInfoSelector = state =>
|
||||
state[MainApp].allChallengesInfo;
|
||||
export const userProfileFetchStateSelector = state =>
|
||||
state[MainApp].userProfileFetchState;
|
||||
export const usernameSelector = state => state[MainApp].appUsername;
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/* eslint-disable @typescript-eslint/no-unsafe-member-access */
|
||||
/* eslint-disable @typescript-eslint/restrict-template-expressions */
|
||||
import { Button, Modal } from '@freecodecamp/react-bootstrap';
|
||||
import { useStaticQuery, graphql } from 'gatsby';
|
||||
import { noop } from 'lodash-es';
|
||||
import React, { Component } from 'react';
|
||||
import { TFunction, withTranslation } from 'react-i18next';
|
||||
@@ -13,8 +12,11 @@ import { dasherize } from '../../../../../utils/slugs';
|
||||
import { isFinalProject } from '../../../../utils/challenge-types';
|
||||
import Login from '../../../components/Header/components/Login';
|
||||
import { executeGA, allowBlockDonationRequests } from '../../../redux/actions';
|
||||
import { isSignedInSelector } from '../../../redux/selectors';
|
||||
import { AllChallengeNode, ChallengeFiles } from '../../../redux/prop-types';
|
||||
import {
|
||||
isSignedInSelector,
|
||||
allChallengesInfoSelector
|
||||
} from '../../../redux/selectors';
|
||||
import { AllChallengesInfo, ChallengeFiles } from '../../../redux/prop-types';
|
||||
|
||||
import { closeModal, submitChallenge } from '../redux/actions';
|
||||
import {
|
||||
@@ -34,6 +36,7 @@ const mapStateToProps = createSelector(
|
||||
completedChallengesIds,
|
||||
isCompletionModalOpenSelector,
|
||||
isSignedInSelector,
|
||||
allChallengesInfoSelector,
|
||||
successMessageSelector,
|
||||
(
|
||||
challengeFiles: ChallengeFiles,
|
||||
@@ -45,6 +48,7 @@ const mapStateToProps = createSelector(
|
||||
completedChallengesIds: string[],
|
||||
isOpen: boolean,
|
||||
isSignedIn: boolean,
|
||||
allChallengesInfo: AllChallengesInfo,
|
||||
message: string
|
||||
) => ({
|
||||
challengeFiles,
|
||||
@@ -54,6 +58,7 @@ const mapStateToProps = createSelector(
|
||||
completedChallengesIds,
|
||||
isOpen,
|
||||
isSignedIn,
|
||||
allChallengesInfo,
|
||||
message
|
||||
})
|
||||
);
|
||||
@@ -118,6 +123,7 @@ interface CompletionModalsProps {
|
||||
id: string;
|
||||
isOpen: boolean;
|
||||
isSignedIn: boolean;
|
||||
allChallengesInfo: AllChallengesInfo;
|
||||
message: string;
|
||||
submitChallenge: () => void;
|
||||
superBlock: string;
|
||||
@@ -324,58 +330,13 @@ interface Options {
|
||||
isFinalProjectBlock: boolean;
|
||||
}
|
||||
|
||||
interface CertificateNode {
|
||||
challenge: {
|
||||
// TODO: use enum
|
||||
certification: string;
|
||||
tests: { id: string }[];
|
||||
};
|
||||
}
|
||||
|
||||
const useCurrentBlockIds = (
|
||||
allChallengesInfo: AllChallengesInfo,
|
||||
block: string,
|
||||
certification: string,
|
||||
options?: Options
|
||||
) => {
|
||||
const {
|
||||
allChallengeNode: { edges: challengeEdges },
|
||||
allCertificateNode: { nodes: certificateNodes }
|
||||
}: {
|
||||
allChallengeNode: AllChallengeNode;
|
||||
allCertificateNode: { nodes: CertificateNode[] };
|
||||
} = useStaticQuery(graphql`
|
||||
query getCurrentBlockNodes {
|
||||
allChallengeNode(
|
||||
sort: {
|
||||
fields: [
|
||||
challenge___superOrder
|
||||
challenge___order
|
||||
challenge___challengeOrder
|
||||
]
|
||||
}
|
||||
) {
|
||||
edges {
|
||||
node {
|
||||
challenge {
|
||||
block
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
allCertificateNode {
|
||||
nodes {
|
||||
challenge {
|
||||
certification
|
||||
tests {
|
||||
id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
`);
|
||||
|
||||
const { challengeEdges, certificateNodes } = allChallengesInfo;
|
||||
const currentCertificateIds = certificateNodes
|
||||
.filter(
|
||||
node => dasherize(node.challenge.certification) === certification
|
||||
@@ -390,6 +351,7 @@ const useCurrentBlockIds = (
|
||||
|
||||
const CompletionModal = (props: CompletionModalsProps) => {
|
||||
const currentBlockIds = useCurrentBlockIds(
|
||||
props.allChallengesInfo,
|
||||
props.block || '',
|
||||
props.certification || '',
|
||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
||||
|
||||
Reference in New Issue
Block a user