refactor: stop spreading challenge over the node (#44499)
* refactor: stop spreading challenge over the node Instead of creating a Gatsby node with id of challenge.id, we create a single node field that has all the challenge data. While this makes the GraphQL queries more verbose, it means we're free to create multiple nodes with the same challenge.id. * test: update time-line test for new GQL schema * test: update mocks with new GQL schema
This commit is contained in:
committed by
GitHub
parent
755f27093c
commit
3b560deab6
@ -20,13 +20,6 @@ const createByIdentityMap = {
|
|||||||
|
|
||||||
exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
|
exports.onCreateNode = function onCreateNode({ node, actions, getNode }) {
|
||||||
const { createNodeField } = actions;
|
const { createNodeField } = actions;
|
||||||
if (node.internal.type === 'ChallengeNode') {
|
|
||||||
const { tests = [], block, dashedName, superBlock } = node;
|
|
||||||
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
|
|
||||||
createNodeField({ node, name: 'slug', value: slug });
|
|
||||||
createNodeField({ node, name: 'blockName', value: blockNameify(block) });
|
|
||||||
createNodeField({ node, name: 'tests', value: tests });
|
|
||||||
}
|
|
||||||
|
|
||||||
if (node.internal.type === 'MarkdownRemark') {
|
if (node.internal.type === 'MarkdownRemark') {
|
||||||
const slug = createFilePath({ node, getNode });
|
const slug = createFilePath({ node, getNode });
|
||||||
@ -71,37 +64,45 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
|
|||||||
graphql(`
|
graphql(`
|
||||||
{
|
{
|
||||||
allChallengeNode(
|
allChallengeNode(
|
||||||
sort: { fields: [superOrder, order, challengeOrder] }
|
sort: {
|
||||||
|
fields: [
|
||||||
|
challenge___superOrder
|
||||||
|
challenge___order
|
||||||
|
challenge___challengeOrder
|
||||||
|
]
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
block
|
challenge {
|
||||||
challengeType
|
block
|
||||||
fields {
|
challengeType
|
||||||
slug
|
fields {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
id
|
||||||
|
order
|
||||||
|
required {
|
||||||
|
link
|
||||||
|
src
|
||||||
|
}
|
||||||
|
challengeOrder
|
||||||
|
challengeFiles {
|
||||||
|
name
|
||||||
|
ext
|
||||||
|
contents
|
||||||
|
head
|
||||||
|
tail
|
||||||
|
}
|
||||||
|
solutions {
|
||||||
|
contents
|
||||||
|
ext
|
||||||
|
}
|
||||||
|
superBlock
|
||||||
|
superOrder
|
||||||
|
template
|
||||||
|
usesMultifileEditor
|
||||||
}
|
}
|
||||||
id
|
|
||||||
order
|
|
||||||
required {
|
|
||||||
link
|
|
||||||
src
|
|
||||||
}
|
|
||||||
challengeOrder
|
|
||||||
challengeFiles {
|
|
||||||
name
|
|
||||||
ext
|
|
||||||
contents
|
|
||||||
head
|
|
||||||
tail
|
|
||||||
}
|
|
||||||
solutions {
|
|
||||||
contents
|
|
||||||
ext
|
|
||||||
}
|
|
||||||
superBlock
|
|
||||||
superOrder
|
|
||||||
template
|
|
||||||
usesMultifileEditor
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -137,12 +138,22 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
|
|||||||
);
|
);
|
||||||
|
|
||||||
const blocks = uniq(
|
const blocks = uniq(
|
||||||
result.data.allChallengeNode.edges.map(({ node: { block } }) => block)
|
result.data.allChallengeNode.edges.map(
|
||||||
|
({
|
||||||
|
node: {
|
||||||
|
challenge: { block }
|
||||||
|
}
|
||||||
|
}) => block
|
||||||
|
)
|
||||||
).map(block => blockNameify(block));
|
).map(block => blockNameify(block));
|
||||||
|
|
||||||
const superBlocks = uniq(
|
const superBlocks = uniq(
|
||||||
result.data.allChallengeNode.edges.map(
|
result.data.allChallengeNode.edges.map(
|
||||||
({ node: { superBlock } }) => superBlock
|
({
|
||||||
|
node: {
|
||||||
|
challenge: { superBlock }
|
||||||
|
}
|
||||||
|
}) => superBlock
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
|
||||||
@ -256,6 +267,9 @@ exports.createSchemaCustomization = ({ actions }) => {
|
|||||||
const { createTypes } = actions;
|
const { createTypes } = actions;
|
||||||
const typeDefs = `
|
const typeDefs = `
|
||||||
type ChallengeNode implements Node {
|
type ChallengeNode implements Node {
|
||||||
|
challenge: Challenge
|
||||||
|
}
|
||||||
|
type Challenge {
|
||||||
challengeFiles: [FileContents]
|
challengeFiles: [FileContents]
|
||||||
notes: String
|
notes: String
|
||||||
url: String
|
url: String
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
const crypto = require('crypto');
|
const crypto = require('crypto');
|
||||||
|
const { blockNameify } = require('../../../utils/block-nameify');
|
||||||
|
|
||||||
function createChallengeNode(challenge, reporter) {
|
function createChallengeNode(challenge, reporter) {
|
||||||
// challengeType 11 is for video challenges (they only have instructions)
|
// challengeType 11 is for video challenges (they only have instructions)
|
||||||
@ -31,6 +32,17 @@ function createChallengeNode(challenge, reporter) {
|
|||||||
type: challenge.challengeType === 7 ? 'CertificateNode' : 'ChallengeNode'
|
type: challenge.challengeType === 7 ? 'CertificateNode' : 'ChallengeNode'
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (internal.type === 'ChallengeNode') {
|
||||||
|
const { tests = [], block, dashedName, superBlock } = challenge;
|
||||||
|
const slug = `/learn/${superBlock}/${block}/${dashedName}`;
|
||||||
|
|
||||||
|
challenge.fields = {
|
||||||
|
slug,
|
||||||
|
blockName: blockNameify(block),
|
||||||
|
tests
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
return JSON.parse(
|
return JSON.parse(
|
||||||
JSON.stringify(
|
JSON.stringify(
|
||||||
Object.assign(
|
Object.assign(
|
||||||
@ -41,7 +53,8 @@ function createChallengeNode(challenge, reporter) {
|
|||||||
internal,
|
internal,
|
||||||
sourceInstanceName: 'challenge'
|
sourceInstanceName: 'challenge'
|
||||||
},
|
},
|
||||||
challenge
|
{ challenge },
|
||||||
|
{ id: crypto.randomUUID() }
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
|
@ -1,136 +1,158 @@
|
|||||||
const mockChallengeNodes = [
|
const mockChallengeNodes = [
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-one/block-a/challenge-one',
|
fields: {
|
||||||
blockName: 'Block A'
|
slug: '/super-block-one/block-a/challenge-one',
|
||||||
},
|
blockName: 'Block A'
|
||||||
id: 'a',
|
},
|
||||||
block: 'block-a',
|
id: 'a',
|
||||||
title: 'Challenge One',
|
block: 'block-a',
|
||||||
isPrivate: false,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-one',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-one',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-one/block-a/challenge-two',
|
fields: {
|
||||||
blockName: 'Block A'
|
slug: '/super-block-one/block-a/challenge-two',
|
||||||
},
|
blockName: 'Block A'
|
||||||
id: 'b',
|
},
|
||||||
block: 'block-a',
|
id: 'b',
|
||||||
title: 'Challenge Two',
|
block: 'block-a',
|
||||||
isPrivate: false,
|
title: 'Challenge Two',
|
||||||
superBlock: 'super-block-one',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-two'
|
superBlock: 'super-block-one',
|
||||||
|
dashedName: 'challenge-two'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-one/block-b/challenge-one',
|
fields: {
|
||||||
blockName: 'Block B'
|
slug: '/super-block-one/block-b/challenge-one',
|
||||||
},
|
blockName: 'Block B'
|
||||||
id: 'c',
|
},
|
||||||
block: 'block-b',
|
id: 'c',
|
||||||
title: 'Challenge One',
|
block: 'block-b',
|
||||||
isPrivate: false,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-one',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-one',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-one/block-b/challenge-two',
|
fields: {
|
||||||
blockName: 'Block B'
|
slug: '/super-block-one/block-b/challenge-two',
|
||||||
},
|
blockName: 'Block B'
|
||||||
|
},
|
||||||
|
|
||||||
id: 'd',
|
id: 'd',
|
||||||
block: 'block-b',
|
block: 'block-b',
|
||||||
title: 'Challenge Two',
|
title: 'Challenge Two',
|
||||||
isPrivate: false,
|
isPrivate: false,
|
||||||
superBlock: 'super-block-one',
|
superBlock: 'super-block-one',
|
||||||
dashedName: 'challenge-two'
|
dashedName: 'challenge-two'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-one/block-c/challenge-one',
|
fields: {
|
||||||
blockName: 'Block C'
|
slug: '/super-block-one/block-c/challenge-one',
|
||||||
},
|
blockName: 'Block C'
|
||||||
id: 'e',
|
},
|
||||||
block: 'block-c',
|
id: 'e',
|
||||||
title: 'Challenge One',
|
block: 'block-c',
|
||||||
isPrivate: true,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-one',
|
isPrivate: true,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-one',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-two/block-a/challenge-one',
|
fields: {
|
||||||
blockName: 'Block A'
|
slug: '/super-block-two/block-a/challenge-one',
|
||||||
},
|
blockName: 'Block A'
|
||||||
id: 'f',
|
},
|
||||||
block: 'block-a',
|
id: 'f',
|
||||||
title: 'Challenge One',
|
block: 'block-a',
|
||||||
isPrivate: false,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-two',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-two',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-two/block-a/challenge-two',
|
fields: {
|
||||||
blockName: 'Block A'
|
slug: '/super-block-two/block-a/challenge-two',
|
||||||
},
|
blockName: 'Block A'
|
||||||
id: 'g',
|
},
|
||||||
block: 'block-a',
|
id: 'g',
|
||||||
title: 'Challenge Two',
|
block: 'block-a',
|
||||||
isPrivate: false,
|
title: 'Challenge Two',
|
||||||
superBlock: 'super-block-two',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-two'
|
superBlock: 'super-block-two',
|
||||||
|
dashedName: 'challenge-two'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-two/block-b/challenge-one',
|
fields: {
|
||||||
blockName: 'Block B'
|
slug: '/super-block-two/block-b/challenge-one',
|
||||||
},
|
blockName: 'Block B'
|
||||||
id: 'h',
|
},
|
||||||
block: 'block-b',
|
id: 'h',
|
||||||
title: 'Challenge One',
|
block: 'block-b',
|
||||||
isPrivate: false,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-two',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-two',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-two/block-b/challenge-two',
|
fields: {
|
||||||
blockName: 'Block B'
|
slug: '/super-block-two/block-b/challenge-two',
|
||||||
},
|
blockName: 'Block B'
|
||||||
id: 'i',
|
},
|
||||||
block: 'block-b',
|
id: 'i',
|
||||||
title: 'Challenge Two',
|
block: 'block-b',
|
||||||
isPrivate: false,
|
title: 'Challenge Two',
|
||||||
superBlock: 'super-block-two',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-two'
|
superBlock: 'super-block-two',
|
||||||
|
dashedName: 'challenge-two'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-three/block-a/challenge-one',
|
fields: {
|
||||||
blockName: 'Block A'
|
slug: '/super-block-three/block-a/challenge-one',
|
||||||
},
|
blockName: 'Block A'
|
||||||
id: 'j',
|
},
|
||||||
block: 'block-a',
|
id: 'j',
|
||||||
title: 'Challenge One',
|
block: 'block-a',
|
||||||
isPrivate: false,
|
title: 'Challenge One',
|
||||||
superBlock: 'super-block-three',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-one'
|
superBlock: 'super-block-three',
|
||||||
|
dashedName: 'challenge-one'
|
||||||
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
fields: {
|
challenge: {
|
||||||
slug: '/super-block-three/block-c/challenge-two',
|
fields: {
|
||||||
blockName: 'Block C'
|
slug: '/super-block-three/block-c/challenge-two',
|
||||||
},
|
blockName: 'Block C'
|
||||||
id: 'k',
|
},
|
||||||
block: 'block-c',
|
id: 'k',
|
||||||
title: 'Challenge Two',
|
block: 'block-c',
|
||||||
isPrivate: false,
|
title: 'Challenge Two',
|
||||||
superBlock: 'super-block-three',
|
isPrivate: false,
|
||||||
dashedName: 'challenge-two'
|
superBlock: 'super-block-three',
|
||||||
|
dashedName: 'challenge-two'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -42,19 +42,19 @@ const linkSpacingStyle = {
|
|||||||
|
|
||||||
function renderLandingMap(nodes: ChallengeNode[]) {
|
function renderLandingMap(nodes: ChallengeNode[]) {
|
||||||
nodes = nodes.filter(
|
nodes = nodes.filter(
|
||||||
node => node.superBlock !== SuperBlocks.CodingInterviewPrep
|
({ challenge }) => challenge.superBlock !== SuperBlocks.CodingInterviewPrep
|
||||||
);
|
);
|
||||||
return (
|
return (
|
||||||
<ul data-test-label='certifications'>
|
<ul data-test-label='certifications'>
|
||||||
{nodes.map((node, i) => (
|
{nodes.map(({ challenge }, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<Link
|
<Link
|
||||||
className='btn link-btn btn-lg'
|
className='btn link-btn btn-lg'
|
||||||
to={`/learn/${node.superBlock}/`}
|
to={`/learn/${challenge.superBlock}/`}
|
||||||
>
|
>
|
||||||
<div style={linkSpacingStyle}>
|
<div style={linkSpacingStyle}>
|
||||||
{generateIconComponent(node.superBlock, 'map-icon')}
|
{generateIconComponent(challenge.superBlock, 'map-icon')}
|
||||||
{i18next.t(`intro:${node.superBlock}.title`)}
|
{i18next.t(`intro:${challenge.superBlock}.title`)}
|
||||||
</div>
|
</div>
|
||||||
<LinkButton />
|
<LinkButton />
|
||||||
</Link>
|
</Link>
|
||||||
@ -68,18 +68,20 @@ function renderLearnMap(
|
|||||||
nodes: ChallengeNode[],
|
nodes: ChallengeNode[],
|
||||||
currentSuperBlock: MapProps['currentSuperBlock']
|
currentSuperBlock: MapProps['currentSuperBlock']
|
||||||
) {
|
) {
|
||||||
nodes = nodes.filter(node => node.superBlock !== currentSuperBlock);
|
nodes = nodes.filter(
|
||||||
|
({ challenge }) => challenge.superBlock !== currentSuperBlock
|
||||||
|
);
|
||||||
return curriculumLocale === 'english' ? (
|
return curriculumLocale === 'english' ? (
|
||||||
<ul data-test-label='learn-curriculum-map'>
|
<ul data-test-label='learn-curriculum-map'>
|
||||||
{nodes.map((node, i) => (
|
{nodes.map(({ challenge }, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<Link
|
<Link
|
||||||
className='btn link-btn btn-lg'
|
className='btn link-btn btn-lg'
|
||||||
to={`/learn/${node.superBlock}/`}
|
to={`/learn/${challenge.superBlock}/`}
|
||||||
>
|
>
|
||||||
<div style={linkSpacingStyle}>
|
<div style={linkSpacingStyle}>
|
||||||
{generateIconComponent(node.superBlock, 'map-icon')}
|
{generateIconComponent(challenge.superBlock, 'map-icon')}
|
||||||
{createSuperBlockTitle(node.superBlock)}
|
{createSuperBlockTitle(challenge.superBlock)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@ -88,16 +90,18 @@ function renderLearnMap(
|
|||||||
) : (
|
) : (
|
||||||
<ul data-test-label='learn-curriculum-map'>
|
<ul data-test-label='learn-curriculum-map'>
|
||||||
{nodes
|
{nodes
|
||||||
.filter(node => isAuditedCert(curriculumLocale, node.superBlock))
|
.filter(({ challenge }) =>
|
||||||
.map((node, i) => (
|
isAuditedCert(curriculumLocale, challenge.superBlock)
|
||||||
|
)
|
||||||
|
.map(({ challenge }, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<Link
|
<Link
|
||||||
className='btn link-btn btn-lg'
|
className='btn link-btn btn-lg'
|
||||||
to={`/learn/${node.superBlock}/`}
|
to={`/learn/${challenge.superBlock}/`}
|
||||||
>
|
>
|
||||||
<div style={linkSpacingStyle}>
|
<div style={linkSpacingStyle}>
|
||||||
{generateIconComponent(node.superBlock, 'map-icon')}
|
{generateIconComponent(challenge.superBlock, 'map-icon')}
|
||||||
{createSuperBlockTitle(node.superBlock)}
|
{createSuperBlockTitle(challenge.superBlock)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@ -115,16 +119,19 @@ function renderLearnMap(
|
|||||||
<Spacer />
|
<Spacer />
|
||||||
</div>
|
</div>
|
||||||
{nodes
|
{nodes
|
||||||
.filter(node => !isAuditedCert(curriculumLocale, node.superBlock))
|
.filter(
|
||||||
.map((node, i) => (
|
({ challenge }) =>
|
||||||
|
!isAuditedCert(curriculumLocale, challenge.superBlock)
|
||||||
|
)
|
||||||
|
.map(({ challenge }, i) => (
|
||||||
<li key={i}>
|
<li key={i}>
|
||||||
<Link
|
<Link
|
||||||
className='btn link-btn btn-lg'
|
className='btn link-btn btn-lg'
|
||||||
to={`/learn/${node.superBlock}/`}
|
to={`/learn/${challenge.superBlock}/`}
|
||||||
>
|
>
|
||||||
<div style={linkSpacingStyle}>
|
<div style={linkSpacingStyle}>
|
||||||
{generateIconComponent(node.superBlock, 'map-icon')}
|
{generateIconComponent(challenge.superBlock, 'map-icon')}
|
||||||
{createSuperBlockTitle(node.superBlock)}
|
{createSuperBlockTitle(challenge.superBlock)}
|
||||||
</div>
|
</div>
|
||||||
</Link>
|
</Link>
|
||||||
</li>
|
</li>
|
||||||
@ -145,12 +152,14 @@ export function Map({
|
|||||||
const data: MapData = useStaticQuery(graphql`
|
const data: MapData = useStaticQuery(graphql`
|
||||||
query SuperBlockNodes {
|
query SuperBlockNodes {
|
||||||
allChallengeNode(
|
allChallengeNode(
|
||||||
sort: { fields: [superOrder] }
|
sort: { fields: [challenge___superOrder] }
|
||||||
filter: { order: { eq: 0 }, challengeOrder: { eq: 0 } }
|
filter: { challenge: { order: { eq: 0 }, challengeOrder: { eq: 0 } } }
|
||||||
) {
|
) {
|
||||||
nodes {
|
nodes {
|
||||||
superBlock
|
challenge {
|
||||||
dashedName
|
superBlock
|
||||||
|
dashedName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -269,11 +269,13 @@ function useIdToNameMap(): Map<string, string> {
|
|||||||
allChallengeNode {
|
allChallengeNode {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
fields {
|
challenge {
|
||||||
slug
|
fields {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
id
|
||||||
|
title
|
||||||
}
|
}
|
||||||
id
|
|
||||||
title
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -289,12 +291,14 @@ function useIdToNameMap(): Map<string, string> {
|
|||||||
edges.forEach(
|
edges.forEach(
|
||||||
({
|
({
|
||||||
node: {
|
node: {
|
||||||
// @ts-expect-error Graphql needs typing
|
challenge: {
|
||||||
id,
|
// @ts-expect-error Graphql needs typing
|
||||||
// @ts-expect-error Graphql needs typing
|
id,
|
||||||
title,
|
// @ts-expect-error Graphql needs typing
|
||||||
// @ts-expect-error Graphql needs typing
|
title,
|
||||||
fields: { slug }
|
// @ts-expect-error Graphql needs typing
|
||||||
|
fields: { slug }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}) => {
|
}) => {
|
||||||
idToNameMap.set(id, { challengeTitle: title, challengePath: slug });
|
idToNameMap.set(id, { challengeTitle: title, challengePath: slug });
|
||||||
|
@ -12,29 +12,35 @@ beforeEach(() => {
|
|||||||
edges: [
|
edges: [
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
fields: {
|
challenge: {
|
||||||
slug: ''
|
fields: {
|
||||||
},
|
slug: ''
|
||||||
id: '5e46f802ac417301a38fb92b',
|
},
|
||||||
title: 'Page View Time Series Visualizer'
|
id: '5e46f802ac417301a38fb92b',
|
||||||
|
title: 'Page View Time Series Visualizer'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
fields: {
|
challenge: {
|
||||||
slug: ''
|
fields: {
|
||||||
},
|
slug: ''
|
||||||
id: '5e4f5c4b570f7e3a4949899f',
|
},
|
||||||
title: 'Sea Level Predictor'
|
id: '5e4f5c4b570f7e3a4949899f',
|
||||||
|
title: 'Sea Level Predictor'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
node: {
|
node: {
|
||||||
fields: {
|
challenge: {
|
||||||
slug: ''
|
fields: {
|
||||||
},
|
slug: ''
|
||||||
id: '5e46f7f8ac417301a38fb92a',
|
},
|
||||||
title: 'Medical Data Visualizer'
|
id: '5e46f7f8ac417301a38fb92a',
|
||||||
|
title: 'Medical Data Visualizer'
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -53,7 +53,9 @@ interface LearnPageProps {
|
|||||||
user: User;
|
user: User;
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: Slug;
|
challenge: {
|
||||||
|
fields: Slug;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
executeGA: (payload: Record<string, unknown>) => void;
|
executeGA: (payload: Record<string, unknown>) => void;
|
||||||
@ -70,7 +72,9 @@ function LearnPage({
|
|||||||
executeGA,
|
executeGA,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { slug }
|
challenge: {
|
||||||
|
fields: { slug }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}: LearnPageProps) {
|
}: LearnPageProps) {
|
||||||
@ -117,9 +121,11 @@ export default connect(mapStateToProps, mapDispatchToProps)(LearnPage);
|
|||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query FirstChallenge {
|
query FirstChallenge {
|
||||||
challengeNode(order: { eq: 0 }, challengeOrder: { eq: 0 }) {
|
challengeNode(challenge: { order: { eq: 0 }, challengeOrder: { eq: 0 } }) {
|
||||||
fields {
|
challenge {
|
||||||
slug
|
fields {
|
||||||
|
slug
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -128,55 +128,57 @@ export interface VideoLocaleIds {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type ChallengeNode = {
|
export type ChallengeNode = {
|
||||||
block: string;
|
challenge: {
|
||||||
challengeOrder: number;
|
block: string;
|
||||||
challengeType: number;
|
challengeOrder: number;
|
||||||
dashedName: string;
|
challengeType: number;
|
||||||
description: string;
|
dashedName: string;
|
||||||
challengeFiles: ChallengeFiles;
|
|
||||||
fields: Fields;
|
|
||||||
forumTopicId: number;
|
|
||||||
guideUrl: string;
|
|
||||||
head: string[];
|
|
||||||
helpCategory: string;
|
|
||||||
id: string;
|
|
||||||
instructions: string;
|
|
||||||
isComingSoon: boolean;
|
|
||||||
internal?: {
|
|
||||||
content: string;
|
|
||||||
contentDigest: string;
|
|
||||||
description: string;
|
description: string;
|
||||||
fieldOwners: string[];
|
challengeFiles: ChallengeFiles;
|
||||||
ignoreType: boolean | null;
|
fields: Fields;
|
||||||
mediaType: string;
|
forumTopicId: number;
|
||||||
owner: string;
|
guideUrl: string;
|
||||||
type: string;
|
head: string[];
|
||||||
|
helpCategory: string;
|
||||||
|
id: string;
|
||||||
|
instructions: string;
|
||||||
|
isComingSoon: boolean;
|
||||||
|
internal?: {
|
||||||
|
content: string;
|
||||||
|
contentDigest: string;
|
||||||
|
description: string;
|
||||||
|
fieldOwners: string[];
|
||||||
|
ignoreType: boolean | null;
|
||||||
|
mediaType: string;
|
||||||
|
owner: string;
|
||||||
|
type: string;
|
||||||
|
};
|
||||||
|
notes: string;
|
||||||
|
removeComments: boolean;
|
||||||
|
isLocked: boolean;
|
||||||
|
isPrivate: boolean;
|
||||||
|
order: number;
|
||||||
|
question: Question;
|
||||||
|
required: Required[];
|
||||||
|
solutions: {
|
||||||
|
[T in FileKey]: FileKeyChallenge;
|
||||||
|
};
|
||||||
|
sourceInstanceName: string;
|
||||||
|
superOrder: number;
|
||||||
|
superBlock: SuperBlocks;
|
||||||
|
tail: string[];
|
||||||
|
template: string;
|
||||||
|
tests: Test[];
|
||||||
|
time: string;
|
||||||
|
title: string;
|
||||||
|
translationPending: boolean;
|
||||||
|
url: string;
|
||||||
|
usesMultifileEditor: boolean;
|
||||||
|
videoId: string;
|
||||||
|
videoLocaleIds?: VideoLocaleIds;
|
||||||
|
bilibiliIds?: BilibiliIds;
|
||||||
|
videoUrl: string;
|
||||||
};
|
};
|
||||||
notes: string;
|
|
||||||
removeComments: boolean;
|
|
||||||
isLocked: boolean;
|
|
||||||
isPrivate: boolean;
|
|
||||||
order: number;
|
|
||||||
question: Question;
|
|
||||||
required: Required[];
|
|
||||||
solutions: {
|
|
||||||
[T in FileKey]: FileKeyChallenge;
|
|
||||||
};
|
|
||||||
sourceInstanceName: string;
|
|
||||||
superOrder: number;
|
|
||||||
superBlock: SuperBlocks;
|
|
||||||
tail: string[];
|
|
||||||
template: string;
|
|
||||||
tests: Test[];
|
|
||||||
time: string;
|
|
||||||
title: string;
|
|
||||||
translationPending: boolean;
|
|
||||||
url: string;
|
|
||||||
usesMultifileEditor: boolean;
|
|
||||||
videoId: string;
|
|
||||||
videoLocaleIds?: VideoLocaleIds;
|
|
||||||
bilibiliIds?: BilibiliIds;
|
|
||||||
videoUrl: string;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AllChallengeNode = {
|
export type AllChallengeNode = {
|
||||||
|
@ -212,7 +212,9 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title }
|
challengeNode: {
|
||||||
|
challenge: { title }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} = this.props;
|
} = this.props;
|
||||||
this.initializeComponent(title);
|
this.initializeComponent(title);
|
||||||
@ -222,16 +224,20 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
title: prevTitle,
|
challenge: {
|
||||||
fields: { tests: prevTests }
|
title: prevTitle,
|
||||||
|
fields: { tests: prevTests }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = prevProps;
|
} = prevProps;
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
title: currentTitle,
|
challenge: {
|
||||||
fields: { tests: currTests }
|
title: currentTitle,
|
||||||
|
fields: { tests: currTests }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -250,11 +256,13 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|||||||
openModal,
|
openModal,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
challengeFiles,
|
challenge: {
|
||||||
fields: { tests },
|
challengeFiles,
|
||||||
challengeType,
|
fields: { tests },
|
||||||
removeComments,
|
challengeType,
|
||||||
helpCategory
|
removeComments,
|
||||||
|
helpCategory
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: {
|
pageContext: {
|
||||||
@ -282,7 +290,7 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|||||||
cancelTests();
|
cancelTests();
|
||||||
}
|
}
|
||||||
|
|
||||||
getChallenge = () => this.props.data.challengeNode;
|
getChallenge = () => this.props.data.challengeNode.challenge;
|
||||||
|
|
||||||
getBlockNameTitle(t: TFunction) {
|
getBlockNameTitle(t: TFunction) {
|
||||||
const { block, superBlock, title } = this.getChallenge();
|
const { block, superBlock, title } = this.getChallenge();
|
||||||
@ -337,8 +345,10 @@ class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|||||||
challengeFiles,
|
challengeFiles,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { tests },
|
challenge: {
|
||||||
usesMultifileEditor
|
fields: { tests },
|
||||||
|
usesMultifileEditor
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -489,40 +499,42 @@ export default connect(
|
|||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query ClassicChallenge($slug: String!) {
|
query ClassicChallenge($slug: String!) {
|
||||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||||
block
|
challenge {
|
||||||
title
|
block
|
||||||
description
|
title
|
||||||
instructions
|
description
|
||||||
notes
|
instructions
|
||||||
removeComments
|
notes
|
||||||
challengeType
|
removeComments
|
||||||
helpCategory
|
challengeType
|
||||||
videoUrl
|
helpCategory
|
||||||
superBlock
|
videoUrl
|
||||||
translationPending
|
superBlock
|
||||||
forumTopicId
|
translationPending
|
||||||
fields {
|
forumTopicId
|
||||||
blockName
|
fields {
|
||||||
slug
|
blockName
|
||||||
tests {
|
slug
|
||||||
text
|
tests {
|
||||||
testString
|
text
|
||||||
|
testString
|
||||||
|
}
|
||||||
|
}
|
||||||
|
required {
|
||||||
|
link
|
||||||
|
src
|
||||||
|
}
|
||||||
|
usesMultifileEditor
|
||||||
|
challengeFiles {
|
||||||
|
fileKey
|
||||||
|
ext
|
||||||
|
name
|
||||||
|
contents
|
||||||
|
head
|
||||||
|
tail
|
||||||
|
editableRegionBoundaries
|
||||||
}
|
}
|
||||||
}
|
|
||||||
required {
|
|
||||||
link
|
|
||||||
src
|
|
||||||
}
|
|
||||||
usesMultifileEditor
|
|
||||||
challengeFiles {
|
|
||||||
fileKey
|
|
||||||
ext
|
|
||||||
name
|
|
||||||
contents
|
|
||||||
head
|
|
||||||
tail
|
|
||||||
editableRegionBoundaries
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -49,7 +49,9 @@ class ShowCodeAlly extends Component<ShowCodeAllyProps> {
|
|||||||
const {
|
const {
|
||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { challengeType, title }
|
challengeNode: {
|
||||||
|
challenge: { challengeType, title }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta }
|
pageContext: { challengeMeta }
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -60,9 +62,11 @@ class ShowCodeAlly extends Component<ShowCodeAllyProps> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
title,
|
challenge: {
|
||||||
fields: { blockName },
|
title,
|
||||||
url
|
fields: { blockName },
|
||||||
|
url
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
webhookToken = null
|
webhookToken = null
|
||||||
@ -94,12 +98,14 @@ export default connect(mapStateToProps, mapDispatchToProps)(ShowCodeAlly);
|
|||||||
// GraphQL
|
// GraphQL
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query CodeAllyChallenge($slug: String!) {
|
query CodeAllyChallenge($slug: String!) {
|
||||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||||
title
|
challenge {
|
||||||
challengeType
|
title
|
||||||
url
|
challengeType
|
||||||
fields {
|
url
|
||||||
blockName
|
fields {
|
||||||
|
blockName
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -280,13 +280,23 @@ const useCurrentBlockIds = (blockName: string) => {
|
|||||||
allChallengeNode: { edges }
|
allChallengeNode: { edges }
|
||||||
}: { allChallengeNode: AllChallengeNode } = useStaticQuery(graphql`
|
}: { allChallengeNode: AllChallengeNode } = useStaticQuery(graphql`
|
||||||
query getCurrentBlockNodes {
|
query getCurrentBlockNodes {
|
||||||
allChallengeNode(sort: { fields: [superOrder, order, challengeOrder] }) {
|
allChallengeNode(
|
||||||
|
sort: {
|
||||||
|
fields: [
|
||||||
|
challenge___superOrder
|
||||||
|
challenge___order
|
||||||
|
challenge___challengeOrder
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
fields {
|
challenge {
|
||||||
blockName
|
fields {
|
||||||
|
blockName
|
||||||
|
}
|
||||||
|
id
|
||||||
}
|
}
|
||||||
id
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -294,9 +304,9 @@ const useCurrentBlockIds = (blockName: string) => {
|
|||||||
`);
|
`);
|
||||||
|
|
||||||
const currentBlockIds = edges
|
const currentBlockIds = edges
|
||||||
.filter(edge => edge.node.fields.blockName === blockName)
|
.filter(edge => edge.node.challenge.fields.blockName === blockName)
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
.map(edge => edge.node.id);
|
.map(edge => edge.node.challenge.id);
|
||||||
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
||||||
return currentBlockIds;
|
return currentBlockIds;
|
||||||
};
|
};
|
||||||
|
@ -123,16 +123,20 @@ class BackEnd extends Component<BackEndProps> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
title: prevTitle,
|
challenge: {
|
||||||
fields: { tests: prevTests }
|
title: prevTitle,
|
||||||
|
fields: { tests: prevTests }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = prevProps;
|
} = prevProps;
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
title: currentTitle,
|
challenge: {
|
||||||
fields: { tests: currTests }
|
title: currentTitle,
|
||||||
|
fields: { tests: currTests }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} = this.props;
|
} = this.props;
|
||||||
@ -149,10 +153,12 @@ class BackEnd extends Component<BackEndProps> {
|
|||||||
updateChallengeMeta,
|
updateChallengeMeta,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { tests },
|
challenge: {
|
||||||
title,
|
fields: { tests },
|
||||||
challengeType,
|
title,
|
||||||
helpCategory
|
challengeType,
|
||||||
|
helpCategory
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta }
|
pageContext: { challengeMeta }
|
||||||
@ -182,15 +188,17 @@ class BackEnd extends Component<BackEndProps> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { blockName },
|
challenge: {
|
||||||
challengeType,
|
fields: { blockName },
|
||||||
forumTopicId,
|
challengeType,
|
||||||
title,
|
forumTopicId,
|
||||||
description,
|
title,
|
||||||
instructions,
|
description,
|
||||||
translationPending,
|
instructions,
|
||||||
superBlock,
|
translationPending,
|
||||||
block
|
superBlock,
|
||||||
|
block
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChallengeCompleted,
|
isChallengeCompleted,
|
||||||
@ -278,22 +286,24 @@ export default connect(
|
|||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query BackendChallenge($slug: String!) {
|
query BackendChallenge($slug: String!) {
|
||||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||||
forumTopicId
|
challenge {
|
||||||
title
|
forumTopicId
|
||||||
description
|
title
|
||||||
instructions
|
description
|
||||||
challengeType
|
instructions
|
||||||
helpCategory
|
challengeType
|
||||||
superBlock
|
helpCategory
|
||||||
block
|
superBlock
|
||||||
translationPending
|
block
|
||||||
fields {
|
translationPending
|
||||||
blockName
|
fields {
|
||||||
slug
|
blockName
|
||||||
tests {
|
slug
|
||||||
text
|
tests {
|
||||||
testString
|
text
|
||||||
|
testString
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -76,7 +76,9 @@ class Project extends Component<ProjectProps> {
|
|||||||
const {
|
const {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title, challengeType, helpCategory }
|
challengeNode: {
|
||||||
|
challenge: { title, challengeType, helpCategory }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
@ -94,13 +96,17 @@ class Project extends Component<ProjectProps> {
|
|||||||
componentDidUpdate(prevProps: ProjectProps): void {
|
componentDidUpdate(prevProps: ProjectProps): void {
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title: prevTitle }
|
challengeNode: {
|
||||||
|
challenge: { title: prevTitle }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} = prevProps;
|
} = prevProps;
|
||||||
const {
|
const {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title: currentTitle, challengeType, helpCategory }
|
challengeNode: {
|
||||||
|
challenge: { title: currentTitle, challengeType, helpCategory }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
@ -130,15 +136,17 @@ class Project extends Component<ProjectProps> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
challengeType,
|
challenge: {
|
||||||
fields: { blockName },
|
challengeType,
|
||||||
forumTopicId,
|
fields: { blockName },
|
||||||
title,
|
forumTopicId,
|
||||||
description,
|
title,
|
||||||
instructions,
|
description,
|
||||||
superBlock,
|
instructions,
|
||||||
block,
|
superBlock,
|
||||||
translationPending
|
block,
|
||||||
|
translationPending
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
isChallengeCompleted,
|
isChallengeCompleted,
|
||||||
@ -215,19 +223,21 @@ export default connect(
|
|||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query ProjectChallenge($slug: String!) {
|
query ProjectChallenge($slug: String!) {
|
||||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||||
forumTopicId
|
challenge {
|
||||||
title
|
forumTopicId
|
||||||
description
|
title
|
||||||
instructions
|
description
|
||||||
challengeType
|
instructions
|
||||||
helpCategory
|
challengeType
|
||||||
superBlock
|
helpCategory
|
||||||
block
|
superBlock
|
||||||
translationPending
|
block
|
||||||
fields {
|
translationPending
|
||||||
blockName
|
fields {
|
||||||
slug
|
blockName
|
||||||
|
slug
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,7 +97,9 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
|||||||
const {
|
const {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title, challengeType, helpCategory }
|
challengeNode: {
|
||||||
|
challenge: { title, challengeType, helpCategory }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
@ -115,13 +117,17 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
|||||||
componentDidUpdate(prevProps: ShowVideoProps): void {
|
componentDidUpdate(prevProps: ShowVideoProps): void {
|
||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title: prevTitle }
|
challengeNode: {
|
||||||
|
challenge: { title: prevTitle }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} = prevProps;
|
} = prevProps;
|
||||||
const {
|
const {
|
||||||
challengeMounted,
|
challengeMounted,
|
||||||
data: {
|
data: {
|
||||||
challengeNode: { title: currentTitle, challengeType, helpCategory }
|
challengeNode: {
|
||||||
|
challenge: { title: currentTitle, challengeType, helpCategory }
|
||||||
|
}
|
||||||
},
|
},
|
||||||
pageContext: { challengeMeta },
|
pageContext: { challengeMeta },
|
||||||
updateChallengeMeta
|
updateChallengeMeta
|
||||||
@ -169,16 +175,18 @@ class ShowVideo extends Component<ShowVideoProps, ShowVideoState> {
|
|||||||
const {
|
const {
|
||||||
data: {
|
data: {
|
||||||
challengeNode: {
|
challengeNode: {
|
||||||
fields: { blockName },
|
challenge: {
|
||||||
title,
|
fields: { blockName },
|
||||||
description,
|
title,
|
||||||
superBlock,
|
description,
|
||||||
block,
|
superBlock,
|
||||||
translationPending,
|
block,
|
||||||
videoId,
|
translationPending,
|
||||||
videoLocaleIds,
|
videoId,
|
||||||
bilibiliIds,
|
videoLocaleIds,
|
||||||
question: { text, answers, solution }
|
bilibiliIds,
|
||||||
|
question: { text, answers, solution }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
openCompletionModal,
|
openCompletionModal,
|
||||||
@ -313,34 +321,36 @@ export default connect(
|
|||||||
|
|
||||||
export const query = graphql`
|
export const query = graphql`
|
||||||
query VideoChallenge($slug: String!) {
|
query VideoChallenge($slug: String!) {
|
||||||
challengeNode(fields: { slug: { eq: $slug } }) {
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
||||||
videoId
|
challenge {
|
||||||
videoLocaleIds {
|
videoId
|
||||||
espanol
|
videoLocaleIds {
|
||||||
italian
|
espanol
|
||||||
portuguese
|
italian
|
||||||
|
portuguese
|
||||||
|
}
|
||||||
|
bilibiliIds {
|
||||||
|
aid
|
||||||
|
bvid
|
||||||
|
cid
|
||||||
|
}
|
||||||
|
title
|
||||||
|
description
|
||||||
|
challengeType
|
||||||
|
helpCategory
|
||||||
|
superBlock
|
||||||
|
block
|
||||||
|
fields {
|
||||||
|
blockName
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
question {
|
||||||
|
text
|
||||||
|
answers
|
||||||
|
solution
|
||||||
|
}
|
||||||
|
translationPending
|
||||||
}
|
}
|
||||||
bilibiliIds {
|
|
||||||
aid
|
|
||||||
bvid
|
|
||||||
cid
|
|
||||||
}
|
|
||||||
title
|
|
||||||
description
|
|
||||||
challengeType
|
|
||||||
helpCategory
|
|
||||||
superBlock
|
|
||||||
block
|
|
||||||
fields {
|
|
||||||
blockName
|
|
||||||
slug
|
|
||||||
}
|
|
||||||
question {
|
|
||||||
text
|
|
||||||
answers
|
|
||||||
solution
|
|
||||||
}
|
|
||||||
translationPending
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
@ -101,7 +101,7 @@ export class Block extends Component<BlockProps> {
|
|||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
let completedCount = 0;
|
let completedCount = 0;
|
||||||
const challengesWithCompleted = challenges.map(challenge => {
|
const challengesWithCompleted = challenges.map(({ challenge }) => {
|
||||||
const { id } = challenge;
|
const { id } = challenge;
|
||||||
const isCompleted = completedChallengeIds.some(
|
const isCompleted = completedChallengeIds.some(
|
||||||
(completedChallengeId: string) => completedChallengeId === id
|
(completedChallengeId: string) => completedChallengeId === id
|
||||||
@ -112,7 +112,7 @@ export class Block extends Component<BlockProps> {
|
|||||||
return { ...challenge, isCompleted };
|
return { ...challenge, isCompleted };
|
||||||
});
|
});
|
||||||
|
|
||||||
const isProjectBlock = challenges.some(challenge => {
|
const isProjectBlock = challenges.some(({ challenge }) => {
|
||||||
const isJsProject =
|
const isJsProject =
|
||||||
challenge.order === 10 && challenge.challengeType === 5;
|
challenge.order === 10 && challenge.challengeType === 5;
|
||||||
|
|
||||||
|
@ -21,7 +21,7 @@ function renderMenuItems({
|
|||||||
edges?: Array<{ node: ChallengeNode }>;
|
edges?: Array<{ node: ChallengeNode }>;
|
||||||
}) {
|
}) {
|
||||||
return edges
|
return edges
|
||||||
.map(({ node }) => node)
|
.map(({ node: { challenge } }) => challenge)
|
||||||
.map(({ title, fields: { slug } }) => (
|
.map(({ title, fields: { slug } }) => (
|
||||||
<Link key={'intro-' + slug} to={slug}>
|
<Link key={'intro-' + slug} to={slug}>
|
||||||
<ListGroupItem>{title}</ListGroupItem>
|
<ListGroupItem>{title}</ListGroupItem>
|
||||||
@ -42,7 +42,8 @@ function IntroductionPage({
|
|||||||
html,
|
html,
|
||||||
frontmatter: { block }
|
frontmatter: { block }
|
||||||
} = markdownRemark;
|
} = markdownRemark;
|
||||||
const firstLesson = allChallengeNode && allChallengeNode.edges[0].node;
|
const firstLesson =
|
||||||
|
allChallengeNode && allChallengeNode.edges[0].node.challenge;
|
||||||
const firstLessonPath = firstLesson
|
const firstLessonPath = firstLesson
|
||||||
? firstLesson.fields.slug
|
? firstLesson.fields.slug
|
||||||
: '/strange-place';
|
: '/strange-place';
|
||||||
@ -97,15 +98,23 @@ export const query = graphql`
|
|||||||
html
|
html
|
||||||
}
|
}
|
||||||
allChallengeNode(
|
allChallengeNode(
|
||||||
filter: { block: { eq: $block } }
|
filter: { challenge: { block: { eq: $block } } }
|
||||||
sort: { fields: [superOrder, order, challengeOrder] }
|
sort: {
|
||||||
|
fields: [
|
||||||
|
challenge___superOrder
|
||||||
|
challenge___order
|
||||||
|
challenge___challengeOrder
|
||||||
|
]
|
||||||
|
}
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
fields {
|
challenge {
|
||||||
slug
|
fields {
|
||||||
|
slug
|
||||||
|
}
|
||||||
|
title
|
||||||
}
|
}
|
||||||
title
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -141,15 +141,15 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
|
|||||||
if (isSignedIn) {
|
if (isSignedIn) {
|
||||||
// see if currentChallenge is in this superBlock
|
// see if currentChallenge is in this superBlock
|
||||||
const currentChallengeEdge = edges.find(
|
const currentChallengeEdge = edges.find(
|
||||||
edge => edge.node.id === currentChallengeId
|
edge => edge.node.challenge.id === currentChallengeId
|
||||||
);
|
);
|
||||||
|
|
||||||
return currentChallengeEdge
|
return currentChallengeEdge
|
||||||
? currentChallengeEdge.node.block
|
? currentChallengeEdge.node.challenge.block
|
||||||
: edge.node.block;
|
: edge.node.challenge.block;
|
||||||
}
|
}
|
||||||
|
|
||||||
return edge.node.block;
|
return edge.node.challenge.block;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initializeExpandedState = () => {
|
const initializeExpandedState = () => {
|
||||||
@ -173,7 +173,9 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
|
|||||||
} = props;
|
} = props;
|
||||||
|
|
||||||
const nodesForSuperBlock = edges.map(({ node }) => node);
|
const nodesForSuperBlock = edges.map(({ node }) => node);
|
||||||
const blockDashedNames = uniq(nodesForSuperBlock.map(({ block }) => block));
|
const blockDashedNames = uniq(
|
||||||
|
nodesForSuperBlock.map(({ challenge: { block } }) => block)
|
||||||
|
);
|
||||||
const i18nSuperBlock = t(`intro:${superBlock}.title`);
|
const i18nSuperBlock = t(`intro:${superBlock}.title`);
|
||||||
const i18nTitle =
|
const i18nTitle =
|
||||||
superBlock === SuperBlocks.CodingInterviewPrep
|
superBlock === SuperBlocks.CodingInterviewPrep
|
||||||
@ -206,7 +208,7 @@ const SuperBlockIntroductionPage = (props: SuperBlockProp) => {
|
|||||||
<Block
|
<Block
|
||||||
blockDashedName={blockDashedName}
|
blockDashedName={blockDashedName}
|
||||||
challenges={nodesForSuperBlock.filter(
|
challenges={nodesForSuperBlock.filter(
|
||||||
node => node.block === blockDashedName
|
node => node.challenge.block === blockDashedName
|
||||||
)}
|
)}
|
||||||
superBlock={superBlock}
|
superBlock={superBlock}
|
||||||
/>
|
/>
|
||||||
@ -263,22 +265,30 @@ export const query = graphql`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
allChallengeNode(
|
allChallengeNode(
|
||||||
sort: { fields: [superOrder, order, challengeOrder] }
|
sort: {
|
||||||
filter: { superBlock: { eq: $superBlock } }
|
fields: [
|
||||||
|
challenge___superOrder
|
||||||
|
challenge___order
|
||||||
|
challenge___challengeOrder
|
||||||
|
]
|
||||||
|
}
|
||||||
|
filter: { challenge: { superBlock: { eq: $superBlock } } }
|
||||||
) {
|
) {
|
||||||
edges {
|
edges {
|
||||||
node {
|
node {
|
||||||
fields {
|
challenge {
|
||||||
slug
|
fields {
|
||||||
blockName
|
slug
|
||||||
|
blockName
|
||||||
|
}
|
||||||
|
id
|
||||||
|
block
|
||||||
|
challengeType
|
||||||
|
title
|
||||||
|
order
|
||||||
|
superBlock
|
||||||
|
dashedName
|
||||||
}
|
}
|
||||||
id
|
|
||||||
block
|
|
||||||
challengeType
|
|
||||||
title
|
|
||||||
order
|
|
||||||
superBlock
|
|
||||||
dashedName
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -45,12 +45,12 @@ const views = {
|
|||||||
|
|
||||||
function getNextChallengePath(_node, index, nodeArray) {
|
function getNextChallengePath(_node, index, nodeArray) {
|
||||||
const next = nodeArray[index + 1];
|
const next = nodeArray[index + 1];
|
||||||
return next ? next.node.fields.slug : '/learn';
|
return next ? next.node.challenge.fields.slug : '/learn';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPrevChallengePath(_node, index, nodeArray) {
|
function getPrevChallengePath(_node, index, nodeArray) {
|
||||||
const prev = nodeArray[index - 1];
|
const prev = nodeArray[index - 1];
|
||||||
return prev ? prev.node.fields.slug : '/learn';
|
return prev ? prev.node.challenge.fields.slug : '/learn';
|
||||||
}
|
}
|
||||||
|
|
||||||
function getTemplateComponent(challengeType) {
|
function getTemplateComponent(challengeType) {
|
||||||
@ -58,7 +58,7 @@ function getTemplateComponent(challengeType) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
exports.createChallengePages = function (createPage) {
|
exports.createChallengePages = function (createPage) {
|
||||||
return function ({ node: challenge }, index, allChallengeEdges) {
|
return function ({ node: { challenge } }, index, allChallengeEdges) {
|
||||||
const {
|
const {
|
||||||
superBlock,
|
superBlock,
|
||||||
block,
|
block,
|
||||||
@ -103,8 +103,8 @@ function getProjectPreviewConfig(challenge, allChallengeEdges) {
|
|||||||
const { block, challengeOrder, usesMultifileEditor } = challenge;
|
const { block, challengeOrder, usesMultifileEditor } = challenge;
|
||||||
|
|
||||||
const challengesInBlock = allChallengeEdges
|
const challengesInBlock = allChallengeEdges
|
||||||
.filter(({ node }) => node.block === block)
|
.filter(({ node: { challenge } }) => challenge.block === block)
|
||||||
.map(({ node }) => node);
|
.map(({ node: { challenge } }) => challenge);
|
||||||
const lastChallenge = challengesInBlock[challengesInBlock.length - 1];
|
const lastChallenge = challengesInBlock[challengesInBlock.length - 1];
|
||||||
const solutionToLastChallenge = sortChallengeFiles(
|
const solutionToLastChallenge = sortChallengeFiles(
|
||||||
lastChallenge.solutions[0] ?? []
|
lastChallenge.solutions[0] ?? []
|
||||||
|
Reference in New Issue
Block a user