2021-08-02 15:39:40 +02:00
|
|
|
import { graphql } from 'gatsby';
|
2018-12-11 23:05:15 +02:00
|
|
|
import React, { Component } from 'react';
|
2018-04-06 14:51:52 +01:00
|
|
|
import Helmet from 'react-helmet';
|
2021-07-19 17:48:02 +01:00
|
|
|
import { TFunction, withTranslation } from 'react-i18next';
|
2021-08-02 15:39:40 +02:00
|
|
|
import { connect } from 'react-redux';
|
2021-08-12 19:48:28 +01:00
|
|
|
import { HandlerProps } from 'react-reflex';
|
2021-08-02 15:39:40 +02:00
|
|
|
import Media from 'react-responsive';
|
|
|
|
import { bindActionCreators, Dispatch } from 'redux';
|
|
|
|
import { createStructuredSelector } from 'reselect';
|
2021-06-17 13:20:39 -05:00
|
|
|
import store from 'store';
|
2021-08-09 01:30:31 -07:00
|
|
|
import { challengeTypes } from '../../../../utils/challenge-types';
|
2021-08-02 15:39:40 +02:00
|
|
|
import LearnLayout from '../../../components/layouts/learn';
|
2021-12-09 12:42:03 -06:00
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
import {
|
2021-08-12 19:48:28 +01:00
|
|
|
ChallengeFile,
|
2021-09-17 13:37:53 -04:00
|
|
|
ChallengeFiles,
|
2021-11-09 19:51:46 +05:30
|
|
|
ChallengeMeta,
|
|
|
|
ChallengeNode,
|
|
|
|
ResizeProps,
|
2021-09-17 13:37:53 -04:00
|
|
|
Test
|
2021-06-25 09:59:33 -05:00
|
|
|
} from '../../../redux/prop-types';
|
2021-08-02 15:39:40 +02:00
|
|
|
import { isContained } from '../../../utils/is-contained';
|
|
|
|
import ChallengeDescription from '../components/Challenge-Description';
|
|
|
|
import Hotkeys from '../components/Hotkeys';
|
|
|
|
import ResetModal from '../components/ResetModal';
|
|
|
|
import ChallengeTitle from '../components/challenge-title';
|
|
|
|
import CompletionModal from '../components/completion-modal';
|
2021-09-17 13:37:53 -04:00
|
|
|
import HelpModal from '../components/help-modal';
|
2021-12-09 12:42:03 -06:00
|
|
|
import Notes from '../components/notes';
|
2021-08-02 15:39:40 +02:00
|
|
|
import Output from '../components/output';
|
2021-09-17 13:37:53 -04:00
|
|
|
import Preview from '../components/preview';
|
2021-11-29 19:30:28 +01:00
|
|
|
import ProjectPreviewModal, {
|
|
|
|
PreviewConfig
|
|
|
|
} from '../components/project-preview-modal';
|
2021-09-17 13:37:53 -04:00
|
|
|
import SidePanel from '../components/side-panel';
|
|
|
|
import VideoModal from '../components/video-modal';
|
2018-04-06 14:51:52 +01:00
|
|
|
import {
|
2021-09-17 13:37:53 -04:00
|
|
|
cancelTests,
|
2018-04-06 14:51:52 +01:00
|
|
|
challengeFilesSelector,
|
2018-05-10 23:25:31 +01:00
|
|
|
challengeMounted,
|
2021-09-17 13:37:53 -04:00
|
|
|
challengeTestsSelector,
|
2019-09-18 17:46:19 +02:00
|
|
|
consoleOutputSelector,
|
2021-09-17 13:37:53 -04:00
|
|
|
createFiles,
|
2020-02-03 15:22:49 +02:00
|
|
|
executeChallenge,
|
2021-09-17 13:37:53 -04:00
|
|
|
initConsole,
|
|
|
|
initTests,
|
|
|
|
isChallengeCompletedSelector,
|
2021-11-29 19:30:28 +01:00
|
|
|
previewMounted,
|
|
|
|
updateChallengeMeta,
|
|
|
|
openModal,
|
|
|
|
setEditorFocusability
|
2018-04-08 23:19:50 +01:00
|
|
|
} from '../redux';
|
2021-08-02 15:39:40 +02:00
|
|
|
import { getGuideUrl } from '../utils';
|
|
|
|
import MultifileEditor from './MultifileEditor';
|
2021-10-19 15:51:38 +01:00
|
|
|
import DesktopLayout from './desktop-layout';
|
2021-11-10 09:29:19 +01:00
|
|
|
import MobileLayout from './mobile-layout';
|
2018-04-06 14:51:52 +01:00
|
|
|
|
|
|
|
import './classic.css';
|
2018-09-21 08:33:19 +03:00
|
|
|
import '../components/test-frame.css';
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
// Redux Setup
|
2018-12-11 23:05:15 +02:00
|
|
|
const mapStateToProps = createStructuredSelector({
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles: challengeFilesSelector,
|
2018-12-11 23:05:15 +02:00
|
|
|
tests: challengeTestsSelector,
|
2021-08-02 15:39:40 +02:00
|
|
|
output: consoleOutputSelector,
|
|
|
|
isChallengeCompleted: isChallengeCompletedSelector
|
2018-12-11 23:05:15 +02:00
|
|
|
});
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
const mapDispatchToProps = (dispatch: Dispatch) =>
|
2018-05-09 12:40:09 +01:00
|
|
|
bindActionCreators(
|
2018-05-10 23:25:31 +01:00
|
|
|
{
|
|
|
|
createFiles,
|
2019-06-20 10:04:36 +02:00
|
|
|
initConsole,
|
2018-05-10 23:25:31 +01:00
|
|
|
initTests,
|
|
|
|
updateChallengeMeta,
|
2019-09-18 17:46:19 +02:00
|
|
|
challengeMounted,
|
2020-02-03 15:22:49 +02:00
|
|
|
executeChallenge,
|
2021-11-29 19:30:28 +01:00
|
|
|
cancelTests,
|
|
|
|
previewMounted,
|
|
|
|
openModal,
|
|
|
|
setEditorFocusability
|
2018-05-10 23:25:31 +01:00
|
|
|
},
|
2018-05-09 12:40:09 +01:00
|
|
|
dispatch
|
|
|
|
);
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
// Types
|
|
|
|
interface ShowClassicProps {
|
|
|
|
cancelTests: () => void;
|
|
|
|
challengeMounted: (arg0: string) => void;
|
2021-08-12 19:48:28 +01:00
|
|
|
createFiles: (arg0: ChallengeFile[]) => void;
|
2021-11-09 19:51:46 +05:30
|
|
|
data: { challengeNode: ChallengeNode };
|
2021-10-13 13:47:59 +02:00
|
|
|
executeChallenge: (options?: { showCompletionModal: boolean }) => void;
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles: ChallengeFiles;
|
2021-06-25 09:59:33 -05:00
|
|
|
initConsole: (arg0: string) => void;
|
2021-08-12 19:48:28 +01:00
|
|
|
initTests: (tests: Test[]) => void;
|
2021-08-02 15:39:40 +02:00
|
|
|
isChallengeCompleted: boolean;
|
2021-06-25 09:59:33 -05:00
|
|
|
output: string[];
|
|
|
|
pageContext: {
|
2021-11-09 19:51:46 +05:30
|
|
|
challengeMeta: ChallengeMeta;
|
2021-11-29 19:30:28 +01:00
|
|
|
projectPreview: PreviewConfig & { showProjectPreview: boolean };
|
2021-06-25 09:59:33 -05:00
|
|
|
};
|
2021-07-19 17:48:02 +01:00
|
|
|
t: TFunction;
|
2021-08-12 19:48:28 +01:00
|
|
|
tests: Test[];
|
2021-11-09 19:51:46 +05:30
|
|
|
updateChallengeMeta: (arg0: ChallengeMeta) => void;
|
2021-11-29 19:30:28 +01:00
|
|
|
openModal: (modal: string) => void;
|
|
|
|
setEditorFocusability: (canFocus: boolean) => void;
|
2021-12-01 20:01:12 +01:00
|
|
|
previewMounted: () => void;
|
2021-06-25 09:59:33 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
interface ShowClassicState {
|
2021-10-19 15:51:38 +01:00
|
|
|
layout: ReflexLayout;
|
2021-06-25 09:59:33 -05:00
|
|
|
resizing: boolean;
|
|
|
|
}
|
|
|
|
|
2021-08-12 19:48:28 +01:00
|
|
|
interface ReflexLayout {
|
2021-06-25 09:59:33 -05:00
|
|
|
codePane: { flex: number };
|
|
|
|
editorPane: { flex: number };
|
|
|
|
instructionPane: { flex: number };
|
2021-12-09 12:42:03 -06:00
|
|
|
notesPane: { flex: number };
|
2021-06-25 09:59:33 -05:00
|
|
|
previewPane: { flex: number };
|
|
|
|
testsPane: { flex: number };
|
|
|
|
}
|
2018-04-06 14:51:52 +01:00
|
|
|
|
2018-12-07 14:08:32 +02:00
|
|
|
const MAX_MOBILE_WIDTH = 767;
|
2021-06-17 13:20:39 -05:00
|
|
|
const REFLEX_LAYOUT = 'challenge-layout';
|
|
|
|
const BASE_LAYOUT = {
|
2021-06-25 09:59:33 -05:00
|
|
|
codePane: { flex: 1 },
|
|
|
|
editorPane: { flex: 1 },
|
|
|
|
instructionPane: { flex: 1 },
|
|
|
|
previewPane: { flex: 0.7 },
|
2021-12-09 12:42:03 -06:00
|
|
|
notesPane: { flex: 0.7 },
|
2021-12-23 14:39:06 +00:00
|
|
|
testsPane: { flex: 0.3 }
|
2021-06-17 13:20:39 -05:00
|
|
|
};
|
2018-12-07 14:08:32 +02:00
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
// Component
|
|
|
|
class ShowClassic extends Component<ShowClassicProps, ShowClassicState> {
|
|
|
|
static displayName: string;
|
2022-02-10 18:31:55 +01:00
|
|
|
containerRef: React.RefObject<HTMLElement>;
|
|
|
|
editorRef: React.RefObject<HTMLElement>;
|
2021-09-17 13:37:53 -04:00
|
|
|
instructionsPanelRef: React.RefObject<HTMLDivElement>;
|
2021-11-09 19:51:46 +05:30
|
|
|
resizeProps: ResizeProps;
|
2021-06-25 09:59:33 -05:00
|
|
|
|
|
|
|
constructor(props: ShowClassicProps) {
|
|
|
|
super(props);
|
2018-05-27 09:37:46 +01:00
|
|
|
|
|
|
|
this.resizeProps = {
|
|
|
|
onStopResize: this.onStopResize.bind(this),
|
|
|
|
onResize: this.onResize.bind(this)
|
2018-05-29 15:34:50 +01:00
|
|
|
};
|
2018-05-27 09:37:46 +01:00
|
|
|
|
2021-07-25 07:41:22 -05:00
|
|
|
// layout: Holds the information of the panes sizes for desktop view
|
2018-05-27 09:37:46 +01:00
|
|
|
this.state = {
|
2021-07-25 07:41:22 -05:00
|
|
|
layout: this.getLayoutState(),
|
2018-05-27 09:37:46 +01:00
|
|
|
resizing: false
|
2018-05-29 15:34:50 +01:00
|
|
|
};
|
2019-09-18 17:46:19 +02:00
|
|
|
|
|
|
|
this.containerRef = React.createRef();
|
2019-10-16 13:53:16 +02:00
|
|
|
this.editorRef = React.createRef();
|
2021-07-12 05:50:07 +02:00
|
|
|
this.instructionsPanelRef = React.createRef();
|
2021-06-17 13:20:39 -05:00
|
|
|
}
|
|
|
|
|
2021-10-19 15:51:38 +01:00
|
|
|
getLayoutState(): ReflexLayout {
|
2021-08-12 19:48:28 +01:00
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
2021-10-19 15:51:38 +01:00
|
|
|
const reflexLayout: ReflexLayout = store.get(REFLEX_LAYOUT);
|
2021-06-17 13:20:39 -05:00
|
|
|
|
|
|
|
// Validate if user has not done any resize of the panes
|
|
|
|
if (!reflexLayout) return BASE_LAYOUT;
|
|
|
|
|
|
|
|
// Check that the layout values stored are valid (exist in base layout). If
|
|
|
|
// not valid, it will fallback to the base layout values and be set on next
|
|
|
|
// user resize.
|
|
|
|
const isValidLayout = isContained(
|
|
|
|
Object.keys(BASE_LAYOUT),
|
|
|
|
Object.keys(reflexLayout)
|
|
|
|
);
|
|
|
|
|
|
|
|
return isValidLayout ? reflexLayout : BASE_LAYOUT;
|
2018-05-27 09:37:46 +01:00
|
|
|
}
|
2021-06-17 13:20:39 -05:00
|
|
|
|
2018-05-27 09:37:46 +01:00
|
|
|
onResize() {
|
2021-07-25 07:41:22 -05:00
|
|
|
this.setState(state => ({ ...state, resizing: true }));
|
2018-05-27 09:37:46 +01:00
|
|
|
}
|
|
|
|
|
2021-08-12 19:48:28 +01:00
|
|
|
onStopResize(event: HandlerProps) {
|
|
|
|
// @ts-expect-error TODO: Apparently, name does not exist on type
|
2021-06-17 13:20:39 -05:00
|
|
|
const { name, flex } = event.component.props;
|
|
|
|
|
|
|
|
// Only interested in tracking layout updates for ReflexElement's
|
|
|
|
if (!name) {
|
2021-07-25 07:41:22 -05:00
|
|
|
this.setState(state => ({ ...state, resizing: false }));
|
2021-06-17 13:20:39 -05:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2021-07-25 07:41:22 -05:00
|
|
|
// Forcing a state update with the value of each panel since on stop resize
|
|
|
|
// is executed per each panel.
|
|
|
|
const newLayout =
|
|
|
|
typeof this.state.layout === 'object'
|
|
|
|
? {
|
|
|
|
...this.state.layout,
|
|
|
|
[name]: { flex }
|
|
|
|
}
|
|
|
|
: this.state.layout;
|
|
|
|
|
|
|
|
this.setState({
|
|
|
|
layout: newLayout,
|
|
|
|
resizing: false
|
|
|
|
});
|
2021-06-17 13:20:39 -05:00
|
|
|
|
2021-07-25 07:41:22 -05:00
|
|
|
store.set(REFLEX_LAYOUT, this.state.layout);
|
2018-05-27 09:37:46 +01:00
|
|
|
}
|
|
|
|
|
2018-04-06 14:51:52 +01:00
|
|
|
componentDidMount() {
|
|
|
|
const {
|
2018-05-24 19:45:38 +01:00
|
|
|
data: {
|
2021-12-14 19:11:20 +01:00
|
|
|
challengeNode: {
|
|
|
|
challenge: { title }
|
|
|
|
}
|
2019-06-20 10:49:53 +02:00
|
|
|
}
|
2018-04-06 14:51:52 +01:00
|
|
|
} = this.props;
|
2019-06-20 10:49:53 +02:00
|
|
|
this.initializeComponent(title);
|
2018-04-06 14:51:52 +01:00
|
|
|
}
|
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
componentDidUpdate(prevProps: ShowClassicProps) {
|
2018-09-30 11:37:19 +01:00
|
|
|
const {
|
|
|
|
data: {
|
2020-11-02 18:22:01 +01:00
|
|
|
challengeNode: {
|
2021-12-14 19:11:20 +01:00
|
|
|
challenge: {
|
|
|
|
title: prevTitle,
|
|
|
|
fields: { tests: prevTests }
|
|
|
|
}
|
2020-11-02 18:22:01 +01:00
|
|
|
}
|
2018-09-30 11:37:19 +01:00
|
|
|
}
|
|
|
|
} = prevProps;
|
2019-06-20 10:49:53 +02:00
|
|
|
const {
|
|
|
|
data: {
|
2020-11-02 18:22:01 +01:00
|
|
|
challengeNode: {
|
2021-12-14 19:11:20 +01:00
|
|
|
challenge: {
|
|
|
|
title: currentTitle,
|
|
|
|
fields: { tests: currTests }
|
|
|
|
}
|
2020-11-02 18:22:01 +01:00
|
|
|
}
|
2019-06-20 10:49:53 +02:00
|
|
|
}
|
|
|
|
} = this.props;
|
2020-11-02 18:22:01 +01:00
|
|
|
if (prevTitle !== currentTitle || prevTests !== currTests) {
|
2019-06-20 10:49:53 +02:00
|
|
|
this.initializeComponent(currentTitle);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
initializeComponent(title: string) {
|
2018-04-06 15:45:49 +01:00
|
|
|
const {
|
2018-05-09 12:40:09 +01:00
|
|
|
challengeMounted,
|
2018-04-06 15:45:49 +01:00
|
|
|
createFiles,
|
2019-06-20 10:04:36 +02:00
|
|
|
initConsole,
|
2018-04-06 15:45:49 +01:00
|
|
|
initTests,
|
|
|
|
updateChallengeMeta,
|
2021-11-29 19:30:28 +01:00
|
|
|
openModal,
|
2018-04-06 15:45:49 +01:00
|
|
|
data: {
|
2018-05-24 19:45:38 +01:00
|
|
|
challengeNode: {
|
2021-12-14 19:11:20 +01:00
|
|
|
challenge: {
|
|
|
|
challengeFiles,
|
|
|
|
fields: { tests },
|
|
|
|
challengeType,
|
|
|
|
removeComments,
|
|
|
|
helpCategory
|
|
|
|
}
|
2018-05-24 19:45:38 +01:00
|
|
|
}
|
2018-04-06 15:45:49 +01:00
|
|
|
},
|
2021-11-29 19:30:28 +01:00
|
|
|
pageContext: {
|
|
|
|
challengeMeta,
|
|
|
|
projectPreview: { showProjectPreview }
|
|
|
|
}
|
2018-04-06 15:45:49 +01:00
|
|
|
} = this.props;
|
2019-06-20 10:49:53 +02:00
|
|
|
initConsole('');
|
2021-08-12 19:48:28 +01:00
|
|
|
createFiles(challengeFiles ?? []);
|
2019-06-20 10:49:53 +02:00
|
|
|
initTests(tests);
|
2021-11-29 19:30:28 +01:00
|
|
|
if (showProjectPreview) openModal('projectPreview');
|
2020-10-30 20:10:34 +01:00
|
|
|
updateChallengeMeta({
|
|
|
|
...challengeMeta,
|
|
|
|
title,
|
2021-04-30 21:30:06 +02:00
|
|
|
removeComments: removeComments !== false,
|
2020-10-30 20:10:34 +01:00
|
|
|
challengeType,
|
|
|
|
helpCategory
|
|
|
|
});
|
2019-06-20 10:49:53 +02:00
|
|
|
challengeMounted(challengeMeta.id);
|
2018-04-06 15:45:49 +01:00
|
|
|
}
|
|
|
|
|
2019-02-06 14:30:54 +03:00
|
|
|
componentWillUnmount() {
|
2020-02-03 15:22:49 +02:00
|
|
|
const { createFiles, cancelTests } = this.props;
|
2021-08-12 19:48:28 +01:00
|
|
|
createFiles([]);
|
2020-02-03 15:22:49 +02:00
|
|
|
cancelTests();
|
2019-02-06 14:30:54 +03:00
|
|
|
}
|
|
|
|
|
2021-12-14 19:11:20 +01:00
|
|
|
getChallenge = () => this.props.data.challengeNode.challenge;
|
2018-12-11 23:05:15 +02:00
|
|
|
|
2021-12-01 07:53:37 -08:00
|
|
|
getBlockNameTitle(t: TFunction) {
|
|
|
|
const { block, superBlock, title } = this.getChallenge();
|
|
|
|
return `${t(`intro:${superBlock}.blocks.${block}.title`)}: ${title}`;
|
2018-12-07 14:08:32 +02:00
|
|
|
}
|
|
|
|
|
2018-12-11 23:05:15 +02:00
|
|
|
getVideoUrl = () => this.getChallenge().videoUrl;
|
|
|
|
|
2018-12-07 14:08:32 +02:00
|
|
|
hasPreview() {
|
2018-12-11 23:05:15 +02:00
|
|
|
const { challengeType } = this.getChallenge();
|
2018-12-07 14:08:32 +02:00
|
|
|
return (
|
|
|
|
challengeType === challengeTypes.html ||
|
2022-01-06 07:26:54 -06:00
|
|
|
challengeType === challengeTypes.modern ||
|
|
|
|
challengeType === challengeTypes.multiFileCertProject
|
2018-12-07 14:08:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2021-06-25 09:59:33 -05:00
|
|
|
renderInstructionsPanel({ showToolPanel }: { showToolPanel: boolean }) {
|
2022-01-06 07:26:54 -06:00
|
|
|
const {
|
|
|
|
block,
|
|
|
|
challengeType,
|
|
|
|
description,
|
|
|
|
forumTopicId,
|
|
|
|
instructions,
|
|
|
|
superBlock,
|
|
|
|
title,
|
|
|
|
translationPending
|
|
|
|
} = this.getChallenge();
|
2018-12-11 23:05:15 +02:00
|
|
|
|
2022-01-06 07:26:54 -06:00
|
|
|
const showBreadCrumbs =
|
|
|
|
challengeType !== challengeTypes.multiFileCertProject;
|
2018-12-07 14:08:32 +02:00
|
|
|
return (
|
|
|
|
<SidePanel
|
2021-02-23 05:22:48 +01:00
|
|
|
block={block}
|
2021-08-02 15:39:40 +02:00
|
|
|
challengeDescription={
|
|
|
|
<ChallengeDescription
|
|
|
|
block={block}
|
|
|
|
description={description}
|
|
|
|
instructions={instructions}
|
|
|
|
/>
|
|
|
|
}
|
|
|
|
challengeTitle={
|
|
|
|
<ChallengeTitle
|
|
|
|
block={block}
|
|
|
|
isCompleted={this.props.isChallengeCompleted}
|
2022-01-06 07:26:54 -06:00
|
|
|
showBreadCrumbs={showBreadCrumbs}
|
2021-08-02 15:39:40 +02:00
|
|
|
superBlock={superBlock}
|
|
|
|
translationPending={translationPending}
|
|
|
|
>
|
|
|
|
{title}
|
|
|
|
</ChallengeTitle>
|
|
|
|
}
|
2019-08-28 21:55:56 +05:30
|
|
|
guideUrl={getGuideUrl({ forumTopicId, title })}
|
2021-07-12 05:50:07 +02:00
|
|
|
instructionsPanelRef={this.instructionsPanelRef}
|
2018-12-07 14:08:32 +02:00
|
|
|
showToolPanel={showToolPanel}
|
2018-12-11 23:05:15 +02:00
|
|
|
videoUrl={this.getVideoUrl()}
|
2018-12-07 14:08:32 +02:00
|
|
|
/>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderEditor() {
|
2021-10-13 13:47:59 +02:00
|
|
|
const {
|
2021-12-17 23:21:17 +03:00
|
|
|
pageContext: {
|
|
|
|
projectPreview: { showProjectPreview }
|
|
|
|
},
|
2021-10-13 13:47:59 +02:00
|
|
|
challengeFiles,
|
|
|
|
data: {
|
|
|
|
challengeNode: {
|
2021-12-14 19:11:20 +01:00
|
|
|
challenge: {
|
|
|
|
fields: { tests },
|
|
|
|
usesMultifileEditor
|
|
|
|
}
|
2021-10-13 13:47:59 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} = this.props;
|
2021-07-28 15:29:11 +01:00
|
|
|
const { description, title } = this.getChallenge();
|
2021-06-25 21:16:54 +05:30
|
|
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-return
|
2019-01-23 12:24:05 +03:00
|
|
|
return (
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles && (
|
2020-07-27 15:20:19 +02:00
|
|
|
<MultifileEditor
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles={challengeFiles}
|
2019-09-18 17:46:19 +02:00
|
|
|
containerRef={this.containerRef}
|
2020-07-01 11:36:45 +02:00
|
|
|
description={description}
|
2020-07-27 15:20:19 +02:00
|
|
|
editorRef={this.editorRef}
|
2021-10-13 13:47:59 +02:00
|
|
|
initialTests={tests}
|
2020-07-27 15:20:19 +02:00
|
|
|
resizeProps={this.resizeProps}
|
2021-07-28 15:29:11 +01:00
|
|
|
title={title}
|
2021-10-13 13:47:59 +02:00
|
|
|
usesMultifileEditor={usesMultifileEditor}
|
2021-12-17 23:21:17 +03:00
|
|
|
showProjectPreview={showProjectPreview}
|
2019-09-17 15:32:23 +02:00
|
|
|
/>
|
|
|
|
)
|
2018-12-07 14:08:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderTestOutput() {
|
2020-12-16 02:02:52 -06:00
|
|
|
const { output, t } = this.props;
|
2018-12-07 14:08:32 +02:00
|
|
|
return (
|
|
|
|
<Output
|
2019-01-23 12:24:05 +03:00
|
|
|
defaultOutput={`
|
2018-05-18 14:54:21 +01:00
|
|
|
/**
|
2020-12-16 02:02:52 -06:00
|
|
|
* ${t('learn.test-output')}
|
2018-05-18 14:54:21 +01:00
|
|
|
*/
|
|
|
|
`}
|
2019-01-27 17:15:21 +03:00
|
|
|
output={output}
|
2018-12-07 14:08:32 +02:00
|
|
|
/>
|
2018-09-30 11:37:19 +01:00
|
|
|
);
|
2018-12-07 14:08:32 +02:00
|
|
|
}
|
|
|
|
|
2021-12-09 12:42:03 -06:00
|
|
|
renderNotes(notes?: string) {
|
|
|
|
return <Notes notes={notes} />;
|
|
|
|
}
|
|
|
|
|
2018-12-07 14:08:32 +02:00
|
|
|
renderPreview() {
|
2018-04-06 14:51:52 +01:00
|
|
|
return (
|
2021-11-29 19:30:28 +01:00
|
|
|
<Preview
|
|
|
|
className='full-height'
|
|
|
|
disableIframe={this.state.resizing}
|
2021-12-01 20:01:12 +01:00
|
|
|
previewMounted={this.props.previewMounted}
|
2021-11-29 19:30:28 +01:00
|
|
|
/>
|
2018-12-07 14:08:32 +02:00
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
render() {
|
2019-11-26 22:14:44 +05:30
|
|
|
const {
|
2021-03-23 15:29:16 -05:00
|
|
|
block,
|
2022-01-06 07:26:54 -06:00
|
|
|
challengeType,
|
2019-11-26 22:14:44 +05:30
|
|
|
fields: { blockName },
|
|
|
|
forumTopicId,
|
2021-12-15 07:52:44 -06:00
|
|
|
hasEditableBoundaries,
|
2021-03-23 15:29:16 -05:00
|
|
|
superBlock,
|
2021-12-20 10:36:31 -08:00
|
|
|
certification,
|
2021-10-13 13:47:59 +02:00
|
|
|
title,
|
2021-12-09 12:42:03 -06:00
|
|
|
usesMultifileEditor,
|
|
|
|
notes
|
2019-11-26 22:14:44 +05:30
|
|
|
} = this.getChallenge();
|
2019-09-18 17:46:19 +02:00
|
|
|
const {
|
|
|
|
executeChallenge,
|
|
|
|
pageContext: {
|
2021-11-29 19:30:28 +01:00
|
|
|
challengeMeta: { nextChallengePath, prevChallengePath },
|
|
|
|
projectPreview
|
2020-08-12 13:11:17 +03:00
|
|
|
},
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles,
|
2020-12-16 02:02:52 -06:00
|
|
|
t
|
2019-09-18 17:46:19 +02:00
|
|
|
} = this.props;
|
2020-08-12 13:11:17 +03:00
|
|
|
|
2018-12-07 14:08:32 +02:00
|
|
|
return (
|
2019-09-18 17:46:19 +02:00
|
|
|
<Hotkeys
|
2019-10-16 13:53:16 +02:00
|
|
|
editorRef={this.editorRef}
|
2019-09-18 17:46:19 +02:00
|
|
|
executeChallenge={executeChallenge}
|
|
|
|
innerRef={this.containerRef}
|
2021-07-12 05:50:07 +02:00
|
|
|
instructionsPanelRef={this.instructionsPanelRef}
|
2019-09-18 17:46:19 +02:00
|
|
|
nextChallengePath={nextChallengePath}
|
|
|
|
prevChallengePath={prevChallengePath}
|
2021-10-13 13:47:59 +02:00
|
|
|
usesMultifileEditor={usesMultifileEditor}
|
2019-09-18 17:46:19 +02:00
|
|
|
>
|
|
|
|
<LearnLayout>
|
2021-12-01 07:53:37 -08:00
|
|
|
<Helmet title={`${this.getBlockNameTitle(t)} | freeCodeCamp.org`} />
|
2019-09-18 17:46:19 +02:00
|
|
|
<Media maxWidth={MAX_MOBILE_WIDTH}>
|
|
|
|
<MobileLayout
|
|
|
|
editor={this.renderEditor()}
|
|
|
|
guideUrl={getGuideUrl({ forumTopicId, title })}
|
2021-12-15 07:52:44 -06:00
|
|
|
hasEditableBoundaries={hasEditableBoundaries}
|
2021-12-09 12:42:03 -06:00
|
|
|
hasNotes={!!notes}
|
2019-09-18 17:46:19 +02:00
|
|
|
hasPreview={this.hasPreview()}
|
|
|
|
instructions={this.renderInstructionsPanel({
|
|
|
|
showToolPanel: false
|
|
|
|
})}
|
2021-12-09 12:42:03 -06:00
|
|
|
notes={this.renderNotes(notes)}
|
2019-09-18 17:46:19 +02:00
|
|
|
preview={this.renderPreview()}
|
|
|
|
testOutput={this.renderTestOutput()}
|
2021-12-02 21:05:54 +01:00
|
|
|
usesMultifileEditor={usesMultifileEditor}
|
2019-09-18 17:46:19 +02:00
|
|
|
videoUrl={this.getVideoUrl()}
|
|
|
|
/>
|
|
|
|
</Media>
|
|
|
|
<Media minWidth={MAX_MOBILE_WIDTH + 1}>
|
|
|
|
<DesktopLayout
|
2021-07-28 15:29:11 +01:00
|
|
|
block={block}
|
2021-08-12 19:48:28 +01:00
|
|
|
challengeFiles={challengeFiles}
|
2022-01-06 07:26:54 -06:00
|
|
|
challengeType={challengeType}
|
2019-09-18 17:46:19 +02:00
|
|
|
editor={this.renderEditor()}
|
2021-12-15 07:52:44 -06:00
|
|
|
hasEditableBoundaries={hasEditableBoundaries}
|
2021-12-09 12:42:03 -06:00
|
|
|
hasNotes={!!notes}
|
2019-09-18 17:46:19 +02:00
|
|
|
hasPreview={this.hasPreview()}
|
|
|
|
instructions={this.renderInstructionsPanel({
|
|
|
|
showToolPanel: true
|
|
|
|
})}
|
2021-07-25 07:41:22 -05:00
|
|
|
layoutState={this.state.layout}
|
2021-12-09 12:42:03 -06:00
|
|
|
notes={this.renderNotes(notes)}
|
2019-09-18 17:46:19 +02:00
|
|
|
preview={this.renderPreview()}
|
|
|
|
resizeProps={this.resizeProps}
|
2021-07-28 15:29:11 +01:00
|
|
|
superBlock={superBlock}
|
2019-09-18 17:46:19 +02:00
|
|
|
testOutput={this.renderTestOutput()}
|
|
|
|
/>
|
|
|
|
</Media>
|
2021-03-23 15:29:16 -05:00
|
|
|
<CompletionModal
|
|
|
|
block={block}
|
|
|
|
blockName={blockName}
|
2021-12-20 10:36:31 -08:00
|
|
|
certification={certification}
|
2021-03-23 15:29:16 -05:00
|
|
|
superBlock={superBlock}
|
|
|
|
/>
|
2019-09-18 17:46:19 +02:00
|
|
|
<HelpModal />
|
|
|
|
<VideoModal videoUrl={this.getVideoUrl()} />
|
|
|
|
<ResetModal />
|
2021-11-29 19:30:28 +01:00
|
|
|
<ProjectPreviewModal previewConfig={projectPreview} />
|
2019-09-18 17:46:19 +02:00
|
|
|
</LearnLayout>
|
|
|
|
</Hotkeys>
|
2018-04-06 14:51:52 +01:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
ShowClassic.displayName = 'ShowClassic';
|
|
|
|
|
2018-09-30 11:37:19 +01:00
|
|
|
export default connect(
|
|
|
|
mapStateToProps,
|
|
|
|
mapDispatchToProps
|
2020-12-16 02:02:52 -06:00
|
|
|
)(withTranslation()(ShowClassic));
|
2018-04-06 14:51:52 +01:00
|
|
|
|
|
|
|
export const query = graphql`
|
|
|
|
query ClassicChallenge($slug: String!) {
|
2021-12-14 19:11:20 +01:00
|
|
|
challengeNode(challenge: { fields: { slug: { eq: $slug } } }) {
|
|
|
|
challenge {
|
|
|
|
block
|
|
|
|
title
|
|
|
|
description
|
2021-12-15 07:52:44 -06:00
|
|
|
hasEditableBoundaries
|
2021-12-14 19:11:20 +01:00
|
|
|
instructions
|
|
|
|
notes
|
|
|
|
removeComments
|
|
|
|
challengeType
|
|
|
|
helpCategory
|
|
|
|
videoUrl
|
|
|
|
superBlock
|
2021-12-20 10:36:31 -08:00
|
|
|
certification
|
2021-12-14 19:11:20 +01:00
|
|
|
translationPending
|
|
|
|
forumTopicId
|
|
|
|
fields {
|
|
|
|
blockName
|
|
|
|
slug
|
|
|
|
tests {
|
|
|
|
text
|
|
|
|
testString
|
|
|
|
}
|
|
|
|
}
|
|
|
|
required {
|
|
|
|
link
|
|
|
|
src
|
|
|
|
}
|
|
|
|
usesMultifileEditor
|
|
|
|
challengeFiles {
|
|
|
|
fileKey
|
|
|
|
ext
|
|
|
|
name
|
|
|
|
contents
|
|
|
|
head
|
|
|
|
tail
|
|
|
|
editableRegionBoundaries
|
2022-01-24 19:42:27 +01:00
|
|
|
history
|
2018-04-06 14:51:52 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
`;
|