feat: make project preview data available for frame
This commit is contained in:
committed by
moT01
parent
8deef3099a
commit
0807a584c4
@ -87,6 +87,13 @@ exports.createPages = function createPages({ graphql, actions, reporter }) {
|
||||
src
|
||||
}
|
||||
challengeOrder
|
||||
challengeFiles {
|
||||
name
|
||||
ext
|
||||
contents
|
||||
head
|
||||
tail
|
||||
}
|
||||
solutions {
|
||||
contents
|
||||
ext
|
||||
|
@ -32,6 +32,7 @@ export const actionTypes = createTypes(
|
||||
'openModal',
|
||||
|
||||
'previewMounted',
|
||||
'projectPreviewMounted',
|
||||
'challengeMounted',
|
||||
'checkChallenge',
|
||||
'executeChallenge',
|
||||
|
@ -236,6 +236,41 @@ function* previewChallengeSaga({ flushLogs = true } = {}) {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: DRY this and previewChallengeSaga
|
||||
// TODO: remove everything about proxyLogger here
|
||||
function* previewProjectSolutionSaga({ payload }) {
|
||||
if (!payload) return;
|
||||
const { isFirstChallengeInBlock, challengeData } = payload;
|
||||
if (!isFirstChallengeInBlock) return;
|
||||
|
||||
const logProxy = yield channel();
|
||||
const proxyLogger = args => logProxy.put(args);
|
||||
|
||||
try {
|
||||
yield fork(takeEveryConsole, logProxy);
|
||||
if (canBuildChallenge(challengeData)) {
|
||||
const challengeMeta = yield select(challengeMetaSelector);
|
||||
const protect = isLoopProtected(challengeMeta);
|
||||
const buildData = yield buildChallengeData(challengeData, {
|
||||
preview: true,
|
||||
protect
|
||||
});
|
||||
if (challengeHasPreview(challengeData)) {
|
||||
const document = yield getContext('document');
|
||||
yield call(updatePreview, buildData, document, proxyLogger, true);
|
||||
}
|
||||
}
|
||||
} catch (err) {
|
||||
if (err === 'timeout') {
|
||||
// TODO: translate the error
|
||||
// eslint-disable-next-line no-ex-assign
|
||||
err = `The code you have written is taking longer than the ${previewTimeout}ms our challenges allow. You may have created an infinite loop or need to write a more efficient algorithm`;
|
||||
}
|
||||
console.log(err);
|
||||
yield put(updateConsole(escape(err)));
|
||||
}
|
||||
}
|
||||
|
||||
export function createExecuteChallengeSaga(types) {
|
||||
return [
|
||||
takeLatest(types.executeChallenge, executeCancellableChallengeSaga),
|
||||
@ -247,6 +282,7 @@ export function createExecuteChallengeSaga(types) {
|
||||
types.resetChallenge
|
||||
],
|
||||
executeCancellablePreviewSaga
|
||||
)
|
||||
),
|
||||
takeLatest(types.projectPreviewMounted, previewProjectSolutionSaga)
|
||||
];
|
||||
}
|
||||
|
@ -39,7 +39,8 @@ const initialState = {
|
||||
completion: false,
|
||||
help: false,
|
||||
video: false,
|
||||
reset: false
|
||||
reset: false,
|
||||
projectPreview: false
|
||||
},
|
||||
projectFormValues: {},
|
||||
successMessage: 'Happy Coding!'
|
||||
@ -114,6 +115,9 @@ export const closeModal = createAction(actionTypes.closeModal);
|
||||
export const openModal = createAction(actionTypes.openModal);
|
||||
|
||||
export const previewMounted = createAction(actionTypes.previewMounted);
|
||||
export const projectPreviewMounted = createAction(
|
||||
actionTypes.projectPreviewMounted
|
||||
);
|
||||
export const challengeMounted = createAction(actionTypes.challengeMounted);
|
||||
export const checkChallenge = createAction(actionTypes.checkChallenge);
|
||||
export const executeChallenge = createAction(actionTypes.executeChallenge);
|
||||
@ -148,6 +152,8 @@ export const isCompletionModalOpenSelector = state =>
|
||||
export const isHelpModalOpenSelector = state => state[ns].modal.help;
|
||||
export const isVideoModalOpenSelector = state => state[ns].modal.video;
|
||||
export const isResetModalOpenSelector = state => state[ns].modal.reset;
|
||||
export const isProjectPreviewModalOpenSelector = state =>
|
||||
state[ns].modal.projectPreview;
|
||||
export const isResettingSelector = state => state[ns].isResetting;
|
||||
|
||||
export const isBuildEnabledSelector = state => state[ns].isBuildEnabled;
|
||||
|
@ -9,7 +9,8 @@ import { getTransformers } from '../rechallenge/transformers';
|
||||
import {
|
||||
createTestFramer,
|
||||
runTestInTestFrame,
|
||||
createMainFramer
|
||||
createMainPreviewFramer,
|
||||
createProjectPreviewFramer
|
||||
} from './frame';
|
||||
import createWorker from './worker-executor';
|
||||
|
||||
@ -197,13 +198,27 @@ export function buildBackendChallenge({ url }) {
|
||||
};
|
||||
}
|
||||
|
||||
export async function updatePreview(buildData, document, proxyLogger) {
|
||||
// TODO: use fewer arguments (and no boolean ones!)
|
||||
export async function updatePreview(
|
||||
buildData,
|
||||
document,
|
||||
proxyLogger,
|
||||
previewProjectSolution
|
||||
) {
|
||||
const { challengeType } = buildData;
|
||||
|
||||
// TODO: no need to await here, nothing is waiting for the preview to be
|
||||
// visible
|
||||
if (challengeType === challengeTypes.html) {
|
||||
await new Promise(resolve =>
|
||||
createMainFramer(document, resolve, proxyLogger)(buildData)
|
||||
);
|
||||
if (previewProjectSolution) {
|
||||
await new Promise(resolve =>
|
||||
createProjectPreviewFramer(document, resolve, proxyLogger)(buildData)
|
||||
);
|
||||
} else {
|
||||
await new Promise(resolve =>
|
||||
createMainPreviewFramer(document, resolve, proxyLogger)(buildData)
|
||||
);
|
||||
}
|
||||
} else {
|
||||
throw new Error(`Cannot show preview for challenge type ${challengeType}`);
|
||||
}
|
||||
|
@ -1,6 +1,7 @@
|
||||
const path = require('path');
|
||||
const { createPoly } = require('../../../utils/polyvinyl');
|
||||
const { dasherize } = require('../../../utils/slugs');
|
||||
|
||||
const { sortChallengeFiles } = require('../../../utils/sort-challengefiles');
|
||||
const { viewTypes } = require('../challenge-types');
|
||||
|
||||
const backend = path.resolve(
|
||||
@ -57,7 +58,7 @@ function getTemplateComponent(challengeType) {
|
||||
}
|
||||
|
||||
exports.createChallengePages = function (createPage) {
|
||||
return function ({ node }, index, allChallenges) {
|
||||
return function ({ node: challenge }, index, allChallengeEdges) {
|
||||
const {
|
||||
superBlock,
|
||||
block,
|
||||
@ -65,17 +66,11 @@ exports.createChallengePages = function (createPage) {
|
||||
required = [],
|
||||
template,
|
||||
challengeType,
|
||||
id,
|
||||
challengeOrder
|
||||
} = node;
|
||||
id
|
||||
} = challenge;
|
||||
// TODO: challengeType === 7 and isPrivate are the same, right? If so, we
|
||||
// should remove one of them.
|
||||
|
||||
const challengesInBlock = allChallenges.filter(
|
||||
({ node }) => node.block === block
|
||||
);
|
||||
const allSolutionsToLastChallenge =
|
||||
challengesInBlock[challengesInBlock.length - 1].node.solutions;
|
||||
createPage({
|
||||
path: slug,
|
||||
component: getTemplateComponent(challengeType),
|
||||
@ -85,18 +80,56 @@ exports.createChallengePages = function (createPage) {
|
||||
block,
|
||||
template,
|
||||
required,
|
||||
nextChallengePath: getNextChallengePath(node, index, allChallenges),
|
||||
prevChallengePath: getPrevChallengePath(node, index, allChallenges),
|
||||
id,
|
||||
isFirstChallengeInBlock: challengeOrder === 0,
|
||||
solutionToLastChallenge: allSolutionsToLastChallenge[0]
|
||||
nextChallengePath: getNextChallengePath(
|
||||
challenge,
|
||||
index,
|
||||
allChallengeEdges
|
||||
),
|
||||
prevChallengePath: getPrevChallengePath(
|
||||
challenge,
|
||||
index,
|
||||
allChallengeEdges
|
||||
),
|
||||
id
|
||||
},
|
||||
projectPreview: getProjectPreviewConfig(challenge, allChallengeEdges),
|
||||
slug
|
||||
}
|
||||
});
|
||||
};
|
||||
};
|
||||
|
||||
function getProjectPreviewConfig(challenge, allChallengeEdges) {
|
||||
const { block, challengeOrder } = challenge;
|
||||
|
||||
const challengesInBlock = allChallengeEdges
|
||||
.filter(({ node }) => node.block === block)
|
||||
.map(({ node }) => node);
|
||||
const lastChallenge = challengesInBlock[challengesInBlock.length - 1];
|
||||
const solutionToLastChallenge = sortChallengeFiles(
|
||||
lastChallenge.solutions[0] ?? []
|
||||
);
|
||||
const lastChallengeFiles = sortChallengeFiles(
|
||||
lastChallenge.challengeFiles ?? []
|
||||
);
|
||||
const projectPreviewChallengeFiles = lastChallengeFiles.map((file, id) =>
|
||||
createPoly({
|
||||
...file,
|
||||
contents: solutionToLastChallenge[id]?.contents ?? file.contents
|
||||
})
|
||||
);
|
||||
|
||||
return {
|
||||
isFirstChallengeInBlock: challengeOrder === 0,
|
||||
challengeData: {
|
||||
challengeType: lastChallenge.challengeType,
|
||||
challengeFiles: projectPreviewChallengeFiles,
|
||||
required: lastChallenge.required,
|
||||
template: lastChallenge.template
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
exports.createBlockIntroPages = function (createPage) {
|
||||
return function (edge) {
|
||||
const {
|
||||
|
Reference in New Issue
Block a user