mirror of
https://github.com/freeCodeCamp/freeCodeCamp.git
synced 2026-05-28 18:26:54 +00:00
feat: use growthbook to determine next and previous challenges (#57435)
This commit is contained in:
committed by
GitHub
parent
3e0b2b914c
commit
827b9e3ecd
+52
-2
@@ -4,8 +4,12 @@ const { createFilePath } = require('gatsby-source-filesystem');
|
|||||||
const uniq = require('lodash/uniq');
|
const uniq = require('lodash/uniq');
|
||||||
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
const MonacoWebpackPlugin = require('monaco-editor-webpack-plugin');
|
||||||
const webpack = require('webpack');
|
const webpack = require('webpack');
|
||||||
const env = require('./config/env.json');
|
|
||||||
|
|
||||||
|
const {
|
||||||
|
superBlockStages,
|
||||||
|
SuperBlockStage
|
||||||
|
} = require('../shared/config/curriculum');
|
||||||
|
const env = require('./config/env.json');
|
||||||
const {
|
const {
|
||||||
createChallengePages,
|
createChallengePages,
|
||||||
createBlockIntroPages,
|
createBlockIntroPages,
|
||||||
@@ -135,8 +139,54 @@ exports.createPages = async function createPages({
|
|||||||
}
|
}
|
||||||
`);
|
`);
|
||||||
|
|
||||||
|
const allChallengeNodes = result.data.allChallengeNode.edges.map(
|
||||||
|
({ node }) => node
|
||||||
|
);
|
||||||
|
|
||||||
|
const inNextCurriculum = superBlock =>
|
||||||
|
superBlockStages[SuperBlockStage.Next].includes(superBlock);
|
||||||
|
|
||||||
|
const currentChallengeNodes = allChallengeNodes.filter(
|
||||||
|
node => !inNextCurriculum(node.challenge.superBlock)
|
||||||
|
);
|
||||||
|
|
||||||
|
const createIdToNextPathMap = nodes =>
|
||||||
|
nodes.reduce((map, node, index) => {
|
||||||
|
const nextNode = nodes[index + 1];
|
||||||
|
const nextPath = nextNode ? nextNode.challenge.fields.slug : null;
|
||||||
|
if (nextPath) map[node.id] = nextPath;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const createIdToPrevPathMap = nodes =>
|
||||||
|
nodes.reduce((map, node, index) => {
|
||||||
|
const prevNode = nodes[index - 1];
|
||||||
|
const prevPath = prevNode ? prevNode.challenge.fields.slug : null;
|
||||||
|
if (prevPath) map[node.id] = prevPath;
|
||||||
|
return map;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const idToNextPathCurrentCurriculum = createIdToNextPathMap(
|
||||||
|
currentChallengeNodes
|
||||||
|
);
|
||||||
|
|
||||||
|
const idToPrevPathCurrentCurriculum = createIdToPrevPathMap(
|
||||||
|
currentChallengeNodes
|
||||||
|
);
|
||||||
|
|
||||||
|
const idToNextPathNextCurriculum = createIdToNextPathMap(allChallengeNodes);
|
||||||
|
|
||||||
|
const idToPrevPathNextCurriculum = createIdToPrevPathMap(allChallengeNodes);
|
||||||
|
|
||||||
// Create challenge pages.
|
// Create challenge pages.
|
||||||
result.data.allChallengeNode.edges.forEach(createChallengePages(createPage));
|
result.data.allChallengeNode.edges.forEach(
|
||||||
|
createChallengePages(createPage, {
|
||||||
|
idToNextPathCurrentCurriculum,
|
||||||
|
idToPrevPathCurrentCurriculum,
|
||||||
|
idToNextPathNextCurriculum,
|
||||||
|
idToPrevPathNextCurriculum
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
const blocks = uniq(
|
const blocks = uniq(
|
||||||
result.data.allChallengeNode.edges.map(
|
result.data.allChallengeNode.edges.map(
|
||||||
|
|||||||
@@ -404,14 +404,17 @@ export type ChallengeMeta = {
|
|||||||
id: string;
|
id: string;
|
||||||
introPath: string;
|
introPath: string;
|
||||||
isFirstStep: boolean;
|
isFirstStep: boolean;
|
||||||
nextChallengePath: string | null;
|
|
||||||
prevChallengePath: string | null;
|
|
||||||
superBlock: SuperBlocks;
|
superBlock: SuperBlocks;
|
||||||
title?: string;
|
title?: string;
|
||||||
challengeType?: number;
|
challengeType?: number;
|
||||||
helpCategory: string;
|
helpCategory: string;
|
||||||
disableLoopProtectTests: boolean;
|
disableLoopProtectTests: boolean;
|
||||||
disableLoopProtectPreview: boolean;
|
disableLoopProtectPreview: boolean;
|
||||||
|
} & NavigationPaths;
|
||||||
|
|
||||||
|
export type NavigationPaths = {
|
||||||
|
nextChallengePath?: string;
|
||||||
|
prevChallengePath?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export type PortfolioProjectData = {
|
export type PortfolioProjectData = {
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
ChallengeMeta,
|
ChallengeMeta,
|
||||||
ChallengeNode,
|
ChallengeNode,
|
||||||
CompletedChallenge,
|
CompletedChallenge,
|
||||||
|
NavigationPaths,
|
||||||
ResizeProps,
|
ResizeProps,
|
||||||
SavedChallengeFiles,
|
SavedChallengeFiles,
|
||||||
Test
|
Test
|
||||||
@@ -62,6 +63,7 @@ import { getGuideUrl } from '../utils';
|
|||||||
import { preloadPage } from '../../../../utils/gatsby/page-loading';
|
import { preloadPage } from '../../../../utils/gatsby/page-loading';
|
||||||
import envData from '../../../../config/env.json';
|
import envData from '../../../../config/env.json';
|
||||||
import ToolPanel from '../components/tool-panel';
|
import ToolPanel from '../components/tool-panel';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import { XtermTerminal } from './xterm';
|
import { XtermTerminal } from './xterm';
|
||||||
import MultifileEditor from './multifile-editor';
|
import MultifileEditor from './multifile-editor';
|
||||||
import DesktopLayout from './desktop-layout';
|
import DesktopLayout from './desktop-layout';
|
||||||
@@ -111,6 +113,7 @@ interface ShowClassicProps extends Pick<PreviewProps, 'previewMounted'> {
|
|||||||
output: string[];
|
output: string[];
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
projectPreview: {
|
projectPreview: {
|
||||||
challengeData: CompletedChallenge;
|
challengeData: CompletedChallenge;
|
||||||
};
|
};
|
||||||
@@ -205,6 +208,7 @@ function ShowClassic({
|
|||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta,
|
challengeMeta,
|
||||||
challengeMeta: { isFirstStep, nextChallengePath },
|
challengeMeta: { isFirstStep, nextChallengePath },
|
||||||
|
nextCurriculumPaths,
|
||||||
projectPreview: { challengeData }
|
projectPreview: { challengeData }
|
||||||
},
|
},
|
||||||
createFiles,
|
createFiles,
|
||||||
@@ -232,6 +236,8 @@ function ShowClassic({
|
|||||||
const isMobile = useMediaQuery({
|
const isMobile = useMediaQuery({
|
||||||
query: `(max-width: ${MAX_MOBILE_WIDTH}px)`
|
query: `(max-width: ${MAX_MOBILE_WIDTH}px)`
|
||||||
});
|
});
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
|
|
||||||
const guideUrl = getGuideUrl({ forumTopicId, title });
|
const guideUrl = getGuideUrl({ forumTopicId, title });
|
||||||
|
|
||||||
const blockNameTitle = `${t(
|
const blockNameTitle = `${t(
|
||||||
@@ -370,11 +376,18 @@ function ShowClassic({
|
|||||||
// project and is shown (once) automatically. In contrast, labs are more
|
// project and is shown (once) automatically. In contrast, labs are more
|
||||||
// freeform, so the preview is shown on demand.
|
// freeform, so the preview is shown on demand.
|
||||||
if (demoType === 'onLoad') openModal('projectPreview');
|
if (demoType === 'onLoad') openModal('projectPreview');
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
|
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
setIsAdvancing(false);
|
setIsAdvancing(false);
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ import { bindActionCreators } from 'redux';
|
|||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { Container, Col, Row, Alert, Spacer } from '@freecodecamp/ui';
|
import { Container, Col, Row, Alert, Spacer } from '@freecodecamp/ui';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
@@ -39,9 +40,11 @@ import {
|
|||||||
ChallengeNode,
|
ChallengeNode,
|
||||||
ChallengeMeta,
|
ChallengeMeta,
|
||||||
CompletedChallenge,
|
CompletedChallenge,
|
||||||
|
NavigationPaths,
|
||||||
Test
|
Test
|
||||||
} from '../../../redux/prop-types';
|
} from '../../../redux/prop-types';
|
||||||
import ProjectToolPanel from '../projects/tool-panel';
|
import ProjectToolPanel from '../projects/tool-panel';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import SolutionForm from '../projects/solution-form';
|
import SolutionForm from '../projects/solution-form';
|
||||||
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||||
import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
import { SuperBlocks } from '../../../../../shared/config/curriculum';
|
||||||
@@ -99,6 +102,7 @@ interface ShowCodeAllyProps {
|
|||||||
openCompletionModal: () => void;
|
openCompletionModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
partiallyCompletedChallenges: CompletedChallenge[];
|
partiallyCompletedChallenges: CompletedChallenge[];
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
@@ -111,6 +115,8 @@ interface ShowCodeAllyProps {
|
|||||||
function ShowCodeAlly(props: ShowCodeAllyProps) {
|
function ShowCodeAlly(props: ShowCodeAllyProps) {
|
||||||
const container = useRef<HTMLElement>(null);
|
const container = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
|
|
||||||
const {
|
const {
|
||||||
completedChallenges,
|
completedChallenges,
|
||||||
data: {
|
data: {
|
||||||
@@ -130,6 +136,7 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
|||||||
},
|
},
|
||||||
isChallengeCompleted,
|
isChallengeCompleted,
|
||||||
isSignedIn,
|
isSignedIn,
|
||||||
|
pageContext: { nextCurriculumPaths },
|
||||||
partiallyCompletedChallenges,
|
partiallyCompletedChallenges,
|
||||||
t,
|
t,
|
||||||
updateSolutionFormValues
|
updateSolutionFormValues
|
||||||
@@ -166,11 +173,17 @@ function ShowCodeAlly(props: ShowCodeAllyProps) {
|
|||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
} = props;
|
} = props;
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ import type { Dispatch } from 'redux';
|
|||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { Container, Col, Alert, Row, Button, Spacer } from '@freecodecamp/ui';
|
import { Container, Col, Alert, Row, Button, Spacer } from '@freecodecamp/ui';
|
||||||
import { micromark } from 'micromark';
|
import { micromark } from 'micromark';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
@@ -46,6 +47,7 @@ import {
|
|||||||
CompletedChallenge,
|
CompletedChallenge,
|
||||||
UserExamQuestion,
|
UserExamQuestion,
|
||||||
UserExam,
|
UserExam,
|
||||||
|
NavigationPaths,
|
||||||
GeneratedExamResults,
|
GeneratedExamResults,
|
||||||
GeneratedExamQuestion,
|
GeneratedExamQuestion,
|
||||||
PrerequisiteChallenge,
|
PrerequisiteChallenge,
|
||||||
@@ -54,6 +56,7 @@ import {
|
|||||||
} from '../../../redux/prop-types';
|
} from '../../../redux/prop-types';
|
||||||
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
import { FlashMessages } from '../../../components/Flash/redux/flash-messages';
|
||||||
import { formatSecondsToTime } from '../../../utils/format-seconds';
|
import { formatSecondsToTime } from '../../../utils/format-seconds';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import ExitExamModal from './components/exit-exam-modal';
|
import ExitExamModal from './components/exit-exam-modal';
|
||||||
import FinishExamModal from './components/finish-exam-modal';
|
import FinishExamModal from './components/finish-exam-modal';
|
||||||
import ExamResults from './components/exam-results';
|
import ExamResults from './components/exam-results';
|
||||||
@@ -127,6 +130,7 @@ interface ShowExamProps {
|
|||||||
closeFinishExamModal: () => void;
|
closeFinishExamModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
startExam: () => void;
|
startExam: () => void;
|
||||||
@@ -164,11 +168,13 @@ function ShowExam(props: ShowExamProps) {
|
|||||||
isChallengeCompleted,
|
isChallengeCompleted,
|
||||||
openExitExamModal,
|
openExitExamModal,
|
||||||
openFinishExamModal,
|
openFinishExamModal,
|
||||||
|
pageContext: { nextCurriculumPaths },
|
||||||
t
|
t
|
||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
let timerInterval: NodeJS.Timeout;
|
let timerInterval: NodeJS.Timeout;
|
||||||
|
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
const container = useRef<HTMLElement>(null);
|
const container = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
const [examTimeInSeconds, setExamTimeInSeconds] = useState(0);
|
const [examTimeInSeconds, setExamTimeInSeconds] = useState(0);
|
||||||
@@ -198,11 +204,17 @@ function ShowExam(props: ShowExamProps) {
|
|||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
} = props;
|
} = props;
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
// Package Utilities
|
// Package Utilities
|
||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { ObserveKeys } from 'react-hotkeys';
|
import { ObserveKeys } from 'react-hotkeys';
|
||||||
import type { TFunction } from 'i18next';
|
import type { TFunction } from 'i18next';
|
||||||
@@ -14,7 +15,12 @@ import ShortcutsModal from '../components/shortcuts-modal';
|
|||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types';
|
import {
|
||||||
|
ChallengeNode,
|
||||||
|
ChallengeMeta,
|
||||||
|
NavigationPaths,
|
||||||
|
Test
|
||||||
|
} from '../../../redux/prop-types';
|
||||||
import Hotkeys from '../components/hotkeys';
|
import Hotkeys from '../components/hotkeys';
|
||||||
import ChallengeTitle from '../components/challenge-title';
|
import ChallengeTitle from '../components/challenge-title';
|
||||||
import ChallegeExplanation from '../components/challenge-explanation';
|
import ChallegeExplanation from '../components/challenge-explanation';
|
||||||
@@ -30,6 +36,7 @@ import {
|
|||||||
initTests
|
initTests
|
||||||
} from '../redux/actions';
|
} from '../redux/actions';
|
||||||
import Scene from '../components/scene/scene';
|
import Scene from '../components/scene/scene';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import { isChallengeCompletedSelector } from '../redux/selectors';
|
import { isChallengeCompletedSelector } from '../redux/selectors';
|
||||||
|
|
||||||
import './show.css';
|
import './show.css';
|
||||||
@@ -64,6 +71,7 @@ interface ShowFillInTheBlankProps {
|
|||||||
openHelpModal: () => void;
|
openHelpModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
||||||
@@ -93,7 +101,7 @@ const ShowFillInTheBlank = ({
|
|||||||
openHelpModal,
|
openHelpModal,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
openCompletionModal,
|
openCompletionModal,
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta, nextCurriculumPaths },
|
||||||
isChallengeCompleted
|
isChallengeCompleted
|
||||||
}: ShowFillInTheBlankProps) => {
|
}: ShowFillInTheBlankProps) => {
|
||||||
const { t } = useTranslation();
|
const { t } = useTranslation();
|
||||||
@@ -109,14 +117,21 @@ const ShowFillInTheBlank = ({
|
|||||||
const [isScenePlaying, setIsScenePlaying] = useState(false);
|
const [isScenePlaying, setIsScenePlaying] = useState(false);
|
||||||
|
|
||||||
const container = useRef<HTMLElement | null>(null);
|
const container = useRef<HTMLElement | null>(null);
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import { graphql } from 'gatsby';
|
import { graphql } from 'gatsby';
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
import Helmet from 'react-helmet';
|
import Helmet from 'react-helmet';
|
||||||
import { useTranslation } from 'react-i18next';
|
import { useTranslation } from 'react-i18next';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
@@ -8,7 +9,12 @@ import { isEqual } from 'lodash';
|
|||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types';
|
import {
|
||||||
|
ChallengeNode,
|
||||||
|
ChallengeMeta,
|
||||||
|
NavigationPaths,
|
||||||
|
Test
|
||||||
|
} from '../../../redux/prop-types';
|
||||||
import ChallengeDescription from '../components/challenge-description';
|
import ChallengeDescription from '../components/challenge-description';
|
||||||
import Hotkeys from '../components/hotkeys';
|
import Hotkeys from '../components/hotkeys';
|
||||||
import ChallengeTitle from '../components/challenge-title';
|
import ChallengeTitle from '../components/challenge-title';
|
||||||
@@ -24,6 +30,7 @@ import {
|
|||||||
} from '../redux/actions';
|
} from '../redux/actions';
|
||||||
import { isChallengeCompletedSelector } from '../redux/selectors';
|
import { isChallengeCompletedSelector } from '../redux/selectors';
|
||||||
import { BlockTypes } from '../../../../../shared/config/blocks';
|
import { BlockTypes } from '../../../../../shared/config/blocks';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import Scene from '../components/scene/scene';
|
import Scene from '../components/scene/scene';
|
||||||
import MultipleChoiceQuestions from '../components/multiple-choice-questions';
|
import MultipleChoiceQuestions from '../components/multiple-choice-questions';
|
||||||
import ChallengeExplanation from '../components/challenge-explanation';
|
import ChallengeExplanation from '../components/challenge-explanation';
|
||||||
@@ -58,6 +65,7 @@ interface ShowQuizProps {
|
|||||||
openHelpModal: () => void;
|
openHelpModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
||||||
updateSolutionFormValues: () => void;
|
updateSolutionFormValues: () => void;
|
||||||
@@ -88,7 +96,7 @@ const ShowGeneric = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta, nextCurriculumPaths },
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
openCompletionModal,
|
openCompletionModal,
|
||||||
@@ -104,11 +112,17 @@ const ShowGeneric = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
@@ -146,6 +160,8 @@ const ShowGeneric = ({
|
|||||||
);
|
);
|
||||||
const [showFeedback, setShowFeedback] = useState(false);
|
const [showFeedback, setShowFeedback] = useState(false);
|
||||||
|
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
|
|
||||||
const handleMcqOptionChange = (
|
const handleMcqOptionChange = (
|
||||||
questionIndex: number,
|
questionIndex: number,
|
||||||
answerIndex: number
|
answerIndex: number
|
||||||
|
|||||||
@@ -7,14 +7,21 @@ import { connect } from 'react-redux';
|
|||||||
import { bindActionCreators } from 'redux';
|
import { bindActionCreators } from 'redux';
|
||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
import { Container, Col, Row, Button, Spacer } from '@freecodecamp/ui';
|
import { Container, Col, Row, Button, Spacer } from '@freecodecamp/ui';
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types';
|
import {
|
||||||
|
ChallengeNode,
|
||||||
|
ChallengeMeta,
|
||||||
|
NavigationPaths,
|
||||||
|
Test
|
||||||
|
} from '../../../redux/prop-types';
|
||||||
import ChallengeDescription from '../components/challenge-description';
|
import ChallengeDescription from '../components/challenge-description';
|
||||||
import Hotkeys from '../components/hotkeys';
|
import Hotkeys from '../components/hotkeys';
|
||||||
import ChallengeTitle from '../components/challenge-title';
|
import ChallengeTitle from '../components/challenge-title';
|
||||||
import CompletionModal from '../components/completion-modal';
|
import CompletionModal from '../components/completion-modal';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import HelpModal from '../components/help-modal';
|
import HelpModal from '../components/help-modal';
|
||||||
import {
|
import {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
@@ -76,6 +83,7 @@ interface MsTrophyProps {
|
|||||||
openHelpModal: () => void;
|
openHelpModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
submitChallenge: () => void;
|
submitChallenge: () => void;
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
@@ -83,6 +91,7 @@ interface MsTrophyProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function MsTrophy(props: MsTrophyProps) {
|
function MsTrophy(props: MsTrophyProps) {
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
const container = useRef<HTMLElement>(null);
|
const container = useRef<HTMLElement>(null);
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
@@ -104,16 +113,22 @@ function MsTrophy(props: MsTrophyProps) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta, nextCurriculumPaths },
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
} = props;
|
} = props;
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import type { TFunction } from 'i18next';
|
|||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
|
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
import LearnLayout from '../../../../components/layouts/learn';
|
import LearnLayout from '../../../../components/layouts/learn';
|
||||||
import { isSignedInSelector } from '../../../../redux/selectors';
|
import { isSignedInSelector } from '../../../../redux/selectors';
|
||||||
import {
|
import {
|
||||||
ChallengeMeta,
|
ChallengeMeta,
|
||||||
ChallengeNode,
|
ChallengeNode,
|
||||||
|
NavigationPaths,
|
||||||
Test
|
Test
|
||||||
} from '../../../../redux/prop-types';
|
} from '../../../../redux/prop-types';
|
||||||
import ChallengeDescription from '../../components/challenge-description';
|
import ChallengeDescription from '../../components/challenge-description';
|
||||||
@@ -34,7 +36,9 @@ import {
|
|||||||
consoleOutputSelector,
|
consoleOutputSelector,
|
||||||
isChallengeCompletedSelector
|
isChallengeCompletedSelector
|
||||||
} from '../../redux/selectors';
|
} from '../../redux/selectors';
|
||||||
|
|
||||||
import { getGuideUrl } from '../../utils';
|
import { getGuideUrl } from '../../utils';
|
||||||
|
import { getChallengePaths } from '../../utils/challenge-paths';
|
||||||
import SolutionForm from '../solution-form';
|
import SolutionForm from '../solution-form';
|
||||||
import ProjectToolPanel from '../tool-panel';
|
import ProjectToolPanel from '../tool-panel';
|
||||||
|
|
||||||
@@ -83,6 +87,7 @@ interface BackEndProps {
|
|||||||
output: string[];
|
output: string[];
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
tests: Test[];
|
tests: Test[];
|
||||||
@@ -92,6 +97,7 @@ interface BackEndProps {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const ShowBackEnd = (props: BackEndProps) => {
|
const ShowBackEnd = (props: BackEndProps) => {
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
const container = useRef<HTMLElement>(null);
|
const container = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
const handleSubmit = ({
|
const handleSubmit = ({
|
||||||
@@ -118,15 +124,21 @@ const ShowBackEnd = (props: BackEndProps) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta }
|
pageContext: { challengeMeta, nextCurriculumPaths }
|
||||||
} = props;
|
} = props;
|
||||||
initConsole();
|
initConsole();
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -8,11 +8,13 @@ import { bindActionCreators } from 'redux';
|
|||||||
import type { Dispatch } from 'redux';
|
import type { Dispatch } from 'redux';
|
||||||
import { createSelector } from 'reselect';
|
import { createSelector } from 'reselect';
|
||||||
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
|
import { Container, Col, Row, Spacer } from '@freecodecamp/ui';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
import LearnLayout from '../../../../components/layouts/learn';
|
import LearnLayout from '../../../../components/layouts/learn';
|
||||||
import {
|
import {
|
||||||
ChallengeNode,
|
ChallengeNode,
|
||||||
ChallengeMeta,
|
ChallengeMeta,
|
||||||
|
NavigationPaths,
|
||||||
Test
|
Test
|
||||||
} from '../../../../redux/prop-types';
|
} from '../../../../redux/prop-types';
|
||||||
import ChallengeDescription from '../../components/challenge-description';
|
import ChallengeDescription from '../../components/challenge-description';
|
||||||
@@ -31,6 +33,7 @@ import { isChallengeCompletedSelector } from '../../redux/selectors';
|
|||||||
import { getGuideUrl } from '../../utils';
|
import { getGuideUrl } from '../../utils';
|
||||||
import SolutionForm from '../solution-form';
|
import SolutionForm from '../solution-form';
|
||||||
import ProjectToolPanel from '../tool-panel';
|
import ProjectToolPanel from '../tool-panel';
|
||||||
|
import { getChallengePaths } from '../../utils/challenge-paths';
|
||||||
|
|
||||||
// Redux Setup
|
// Redux Setup
|
||||||
const mapStateToProps = createSelector(
|
const mapStateToProps = createSelector(
|
||||||
@@ -61,6 +64,7 @@ interface ProjectProps {
|
|||||||
openCompletionModal: () => void;
|
openCompletionModal: () => void;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
t: TFunction;
|
t: TFunction;
|
||||||
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
||||||
@@ -78,6 +82,7 @@ const ShowFrontEndProject = (props: ProjectProps) => {
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
const container = useRef<HTMLElement>(null);
|
const container = useRef<HTMLElement>(null);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@@ -88,16 +93,22 @@ const ShowFrontEndProject = (props: ProjectProps) => {
|
|||||||
challenge: { fields, title, challengeType, helpCategory }
|
challenge: { fields, title, challengeType, helpCategory }
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta, nextCurriculumPaths },
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
} = props;
|
} = props;
|
||||||
initTests(fields.tests);
|
initTests(fields.tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -17,15 +17,22 @@ import {
|
|||||||
useQuiz,
|
useQuiz,
|
||||||
Spacer
|
Spacer
|
||||||
} from '@freecodecamp/ui';
|
} from '@freecodecamp/ui';
|
||||||
|
import { useFeature } from '@growthbook/growthbook-react';
|
||||||
|
|
||||||
// Local Utilities
|
// Local Utilities
|
||||||
import { shuffleArray } from '../../../../../shared/utils/shuffle-array';
|
import { shuffleArray } from '../../../../../shared/utils/shuffle-array';
|
||||||
import LearnLayout from '../../../components/layouts/learn';
|
import LearnLayout from '../../../components/layouts/learn';
|
||||||
import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types';
|
import {
|
||||||
|
ChallengeNode,
|
||||||
|
ChallengeMeta,
|
||||||
|
NavigationPaths,
|
||||||
|
Test
|
||||||
|
} from '../../../redux/prop-types';
|
||||||
import ChallengeDescription from '../components/challenge-description';
|
import ChallengeDescription from '../components/challenge-description';
|
||||||
import Hotkeys from '../components/hotkeys';
|
import Hotkeys from '../components/hotkeys';
|
||||||
import ChallengeTitle from '../components/challenge-title';
|
import ChallengeTitle from '../components/challenge-title';
|
||||||
import CompletionModal from '../components/completion-modal';
|
import CompletionModal from '../components/completion-modal';
|
||||||
|
import { getChallengePaths } from '../utils/challenge-paths';
|
||||||
import {
|
import {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
@@ -74,6 +81,7 @@ interface ShowQuizProps {
|
|||||||
isChallengeCompleted: boolean;
|
isChallengeCompleted: boolean;
|
||||||
pageContext: {
|
pageContext: {
|
||||||
challengeMeta: ChallengeMeta;
|
challengeMeta: ChallengeMeta;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
};
|
};
|
||||||
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
||||||
updateSolutionFormValues: () => void;
|
updateSolutionFormValues: () => void;
|
||||||
@@ -101,7 +109,7 @@ const ShowQuiz = ({
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta, nextCurriculumPaths },
|
||||||
initTests,
|
initTests,
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
isChallengeCompleted,
|
isChallengeCompleted,
|
||||||
@@ -126,6 +134,7 @@ const ShowQuiz = ({
|
|||||||
const [showUnanswered, setShowUnanswered] = useState(false);
|
const [showUnanswered, setShowUnanswered] = useState(false);
|
||||||
|
|
||||||
const [exitConfirmed, setExitConfirmed] = useState(false);
|
const [exitConfirmed, setExitConfirmed] = useState(false);
|
||||||
|
const showNextCurriculum = useFeature('fcc-10').on;
|
||||||
|
|
||||||
const blockNameTitle = `${t(
|
const blockNameTitle = `${t(
|
||||||
`intro:${superBlock}.blocks.${block}.title`
|
`intro:${superBlock}.blocks.${block}.title`
|
||||||
@@ -189,11 +198,17 @@ const ShowQuiz = ({
|
|||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
initTests(tests);
|
initTests(tests);
|
||||||
|
const challengePaths = getChallengePaths({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths: challengeMeta,
|
||||||
|
nextCurriculumPaths
|
||||||
|
});
|
||||||
updateChallengeMeta({
|
updateChallengeMeta({
|
||||||
...challengeMeta,
|
...challengeMeta,
|
||||||
title,
|
title,
|
||||||
challengeType,
|
challengeType,
|
||||||
helpCategory
|
helpCategory,
|
||||||
|
...challengePaths
|
||||||
});
|
});
|
||||||
challengeMounted(challengeMeta.id);
|
challengeMounted(challengeMeta.id);
|
||||||
container.current?.focus();
|
container.current?.focus();
|
||||||
|
|||||||
@@ -0,0 +1,23 @@
|
|||||||
|
import { NavigationPaths } from '../../../redux/prop-types';
|
||||||
|
|
||||||
|
export const getChallengePaths = ({
|
||||||
|
showNextCurriculum,
|
||||||
|
currentCurriculumPaths,
|
||||||
|
nextCurriculumPaths
|
||||||
|
}: {
|
||||||
|
showNextCurriculum: boolean;
|
||||||
|
currentCurriculumPaths: NavigationPaths;
|
||||||
|
nextCurriculumPaths: NavigationPaths;
|
||||||
|
}): NavigationPaths => {
|
||||||
|
const nextChallengePath = showNextCurriculum
|
||||||
|
? nextCurriculumPaths.nextChallengePath
|
||||||
|
: currentCurriculumPaths.nextChallengePath;
|
||||||
|
|
||||||
|
const prevChallengePath = showNextCurriculum
|
||||||
|
? nextCurriculumPaths.prevChallengePath
|
||||||
|
: currentCurriculumPaths.prevChallengePath;
|
||||||
|
return {
|
||||||
|
nextChallengePath,
|
||||||
|
prevChallengePath
|
||||||
|
};
|
||||||
|
};
|
||||||
@@ -72,21 +72,19 @@ function getIsFirstStepInBlock(id, edges) {
|
|||||||
return previous.node.challenge.block !== current.node.challenge.block;
|
return previous.node.challenge.block !== current.node.challenge.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
function getNextChallengePath(id, edges) {
|
|
||||||
const next = edges[id + 1];
|
|
||||||
return next ? next.node.challenge.fields.slug : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPrevChallengePath(id, edges) {
|
|
||||||
const prev = edges[id - 1];
|
|
||||||
return prev ? prev.node.challenge.fields.slug : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getTemplateComponent(challengeType) {
|
function getTemplateComponent(challengeType) {
|
||||||
return views[viewTypes[challengeType]];
|
return views[viewTypes[challengeType]];
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.createChallengePages = function (createPage) {
|
exports.createChallengePages = function (
|
||||||
|
createPage,
|
||||||
|
{
|
||||||
|
idToNextPathCurrentCurriculum,
|
||||||
|
idToPrevPathCurrentCurriculum,
|
||||||
|
idToNextPathNextCurriculum,
|
||||||
|
idToPrevPathNextCurriculum
|
||||||
|
}
|
||||||
|
) {
|
||||||
return function ({ node }, index, allChallengeEdges) {
|
return function ({ node }, index, allChallengeEdges) {
|
||||||
const {
|
const {
|
||||||
dashedName,
|
dashedName,
|
||||||
@@ -121,10 +119,14 @@ exports.createChallengePages = function (createPage) {
|
|||||||
template,
|
template,
|
||||||
required,
|
required,
|
||||||
isLastChallengeInBlock: isLastChallengeInBlock,
|
isLastChallengeInBlock: isLastChallengeInBlock,
|
||||||
nextChallengePath: getNextChallengePath(index, allChallengeEdges),
|
nextChallengePath: idToNextPathCurrentCurriculum[node.id],
|
||||||
prevChallengePath: getPrevChallengePath(index, allChallengeEdges),
|
prevChallengePath: idToPrevPathCurrentCurriculum[node.id],
|
||||||
id
|
id
|
||||||
},
|
},
|
||||||
|
nextCurriculumPaths: {
|
||||||
|
nextChallengePath: idToNextPathNextCurriculum[node.id],
|
||||||
|
prevChallengePath: idToPrevPathNextCurriculum[node.id]
|
||||||
|
},
|
||||||
projectPreview: getProjectPreviewConfig(
|
projectPreview: getProjectPreviewConfig(
|
||||||
node.challenge,
|
node.challenge,
|
||||||
allChallengeEdges
|
allChallengeEdges
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
* but the link of the page isn't rendered on the screen.
|
* but the link of the page isn't rendered on the screen.
|
||||||
* For more details, see https://github.com/freeCodeCamp/freeCodeCamp/pull/55472.
|
* For more details, see https://github.com/freeCodeCamp/freeCodeCamp/pull/55472.
|
||||||
*/
|
*/
|
||||||
export const preloadPage = (path: string | null) => {
|
export const preloadPage = (path?: string) => {
|
||||||
if (!window.___loader || !path) return;
|
if (!window.___loader || !path) return;
|
||||||
|
|
||||||
window.___loader.hovering(path);
|
window.___loader.hovering(path);
|
||||||
|
|||||||
Reference in New Issue
Block a user