diff --git a/client/src/templates/Challenges/dialogue/show.tsx b/client/src/templates/Challenges/dialogue/show.tsx deleted file mode 100644 index fec6814f424..00000000000 --- a/client/src/templates/Challenges/dialogue/show.tsx +++ /dev/null @@ -1,370 +0,0 @@ -// Package Utilities -import { graphql } from 'gatsby'; -import React, { Component } from 'react'; -import Helmet from 'react-helmet'; -import { ObserveKeys } from 'react-hotkeys'; -import type { TFunction } from 'i18next'; -import { withTranslation } from 'react-i18next'; -import { connect } from 'react-redux'; -import { bindActionCreators } from 'redux'; -import type { Dispatch } from 'redux'; -import { createSelector } from 'reselect'; -import { Container, Col, Row, Button } from '@freecodecamp/ui'; -import ShortcutsModal from '../components/shortcuts-modal'; - -// Local Utilities -import Spacer from '../../../components/helpers/spacer'; -import LearnLayout from '../../../components/layouts/learn'; -import { ChallengeNode, ChallengeMeta, Test } from '../../../redux/prop-types'; -import Hotkeys from '../components/hotkeys'; -import CompletionModal from '../components/completion-modal'; -import ChallengeTitle from '../components/challenge-title'; -import HelpModal from '../components/help-modal'; -import PrismFormatted from '../components/prism-formatted'; -import { - challengeMounted, - updateChallengeMeta, - openModal, - initTests -} from '../redux/actions'; -import { isChallengeCompletedSelector } from '../redux/selectors'; -import Scene from '../components/scene/scene'; -import Assignments from '../components/assignments'; - -// Styles -import '../odin/show.css'; -import '../video.css'; - -// Redux Setup -const mapStateToProps = createSelector( - isChallengeCompletedSelector, - (isChallengeCompleted: boolean) => ({ - isChallengeCompleted - }) -); - -const mapDispatchToProps = (dispatch: Dispatch) => - bindActionCreators( - { - initTests, - updateChallengeMeta, - challengeMounted, - openCompletionModal: () => openModal('completion'), - openHelpModal: () => openModal('help') - }, - dispatch - ); - -// Types -interface ShowDialogueProps { - challengeMounted: (arg0: string) => void; - data: { challengeNode: ChallengeNode }; - description: string; - initTests: (xs: Test[]) => void; - isChallengeCompleted: boolean; - openCompletionModal: () => void; - openHelpModal: () => void; - pageContext: { - challengeMeta: ChallengeMeta; - }; - t: TFunction; - updateChallengeMeta: (arg0: ChallengeMeta) => void; -} - -interface ShowDialogueState { - subtitles: string; - downloadURL: string | null; - assignmentsCompleted: number; - allAssignmentsCompleted: boolean; - videoIsLoaded: boolean; - isScenePlaying: boolean; -} - -// Component -class ShowDialogue extends Component { - static displayName: string; - private container: React.RefObject = React.createRef(); - - constructor(props: ShowDialogueProps) { - super(props); - this.state = { - isScenePlaying: false, - subtitles: '', - downloadURL: null, - assignmentsCompleted: 0, - allAssignmentsCompleted: false, - videoIsLoaded: false - }; - - this.handleSubmit = this.handleSubmit.bind(this); - } - - componentDidMount(): void { - const { - challengeMounted, - data: { - challengeNode: { - challenge: { - fields: { tests }, - title, - challengeType, - helpCategory - } - } - }, - pageContext: { challengeMeta }, - initTests, - updateChallengeMeta - } = this.props; - initTests(tests); - updateChallengeMeta({ - ...challengeMeta, - title, - challengeType, - helpCategory - }); - challengeMounted(challengeMeta.id); - this.container.current?.focus(); - } - - componentDidUpdate(prevProps: ShowDialogueProps): void { - const { - data: { - challengeNode: { - challenge: { title: prevTitle } - } - } - } = prevProps; - const { - challengeMounted, - data: { - challengeNode: { - challenge: { title: currentTitle, challengeType, helpCategory } - } - }, - pageContext: { challengeMeta }, - updateChallengeMeta - } = this.props; - if (prevTitle !== currentTitle) { - updateChallengeMeta({ - ...challengeMeta, - title: currentTitle, - challengeType, - helpCategory - }); - challengeMounted(challengeMeta.id); - } - } - - handleSubmit() { - const { openCompletionModal } = this.props; - if (this.state.allAssignmentsCompleted) { - openCompletionModal(); - } - } - - handleAssignmentChange = ( - event: React.ChangeEvent, - totalAssignments: number - ): void => { - const assignmentsCompleted = event.target.checked - ? this.state.assignmentsCompleted + 1 - : this.state.assignmentsCompleted - 1; - const allAssignmentsCompleted = totalAssignments === assignmentsCompleted; - - this.setState({ - assignmentsCompleted, - allAssignmentsCompleted - }); - }; - - onVideoLoad = () => { - this.setState({ - videoIsLoaded: true - }); - }; - - setIsScenePlaying = (shouldPlay: boolean) => { - this.setState({ - isScenePlaying: shouldPlay - }); - }; - - render() { - const { - data: { - challengeNode: { - challenge: { - title, - description, - instructions, - superBlock, - block, - fields: { blockName }, - assignments, - translationPending, - scene - } - } - }, - openHelpModal, - pageContext: { - challengeMeta: { nextChallengePath, prevChallengePath } - }, - isChallengeCompleted, - t - } = this.props; - - const blockNameTitle = `${t( - `intro:${superBlock}.blocks.${block}.title` - )} - ${title}`; - - return ( - this.handleSubmit()} - containerRef={this.container} - nextChallengePath={nextChallengePath} - prevChallengePath={prevChallengePath} - playScene={() => this.setIsScenePlaying(true)} - > - - - - - - - - - {title} - - - - - - {scene && ( - - )} - - - - {instructions && ( - <> - - - - )} - - - - - - - - - - - - - - - - - ); - } -} - -ShowDialogue.displayName = 'ShowDialogue'; - -export default connect( - mapStateToProps, - mapDispatchToProps -)(withTranslation()(ShowDialogue)); - -export const query = graphql` - query Dialogue($id: String!) { - challengeNode(id: { eq: $id }) { - challenge { - videoId - title - description - instructions - challengeType - helpCategory - superBlock - block - fields { - slug - blockName - tests { - text - testString - } - } - translationPending - assignments - scene { - setup { - background - characters { - character - position { - x - y - z - } - opacity - } - audio { - filename - startTime - startTimestamp - finishTimestamp - } - alwaysShowDialogue - } - commands { - background - character - position { - x - y - z - } - opacity - startTime - finishTime - dialogue { - text - align - } - } - } - } - } - } -`; diff --git a/client/src/templates/Challenges/generic/show.tsx b/client/src/templates/Challenges/generic/show.tsx index d40de1a1501..cb067a48471 100644 --- a/client/src/templates/Challenges/generic/show.tsx +++ b/client/src/templates/Challenges/generic/show.tsx @@ -24,6 +24,7 @@ import { } from '../redux/actions'; import { isChallengeCompletedSelector } from '../redux/selectors'; import { BlockTypes } from '../../../../../shared/config/blocks'; +import Scene from '../components/scene/scene'; // Redux Setup const mapStateToProps = (state: unknown) => ({ @@ -69,6 +70,7 @@ const ShowGeneric = ({ instructions, title, translationPending, + scene, superBlock, videoId, videoLocaleIds @@ -127,6 +129,9 @@ const ShowGeneric = ({ setVideoIsLoaded(true); }; + // scene + const [isScenePlaying, setIsScenePlaying] = useState(false); + // assignments const [assignmentsCompleted, setAssignmentsCompleted] = useState(0); const allAssignmentsCompleted = assignmentsCompleted === assignments.length; @@ -151,6 +156,7 @@ const ShowGeneric = ({ containerRef={container} nextChallengePath={nextChallengePath} prevChallengePath={prevChallengePath} + playScene={scene ? () => setIsScenePlaying(true) : undefined} > - - {description && ( + {description && ( + - )} - + + + )} {videoId && ( @@ -185,9 +192,17 @@ const ShowGeneric = ({ )} + {scene && ( + + )} + {instructions && ( - + )} @@ -244,6 +259,43 @@ export const query = graphql` testString } } + scene { + setup { + background + characters { + character + position { + x + y + z + } + opacity + } + audio { + filename + startTime + startTimestamp + finishTimestamp + } + alwaysShowDialogue + } + commands { + background + character + position { + x + y + z + } + opacity + startTime + finishTime + dialogue { + text + align + } + } + } superBlock title translationPending diff --git a/client/utils/gatsby/challenge-page-creator.js b/client/utils/gatsby/challenge-page-creator.js index b2572d77823..55c339ed8ce 100644 --- a/client/utils/gatsby/challenge-page-creator.js +++ b/client/utils/gatsby/challenge-page-creator.js @@ -51,11 +51,6 @@ const msTrophy = path.resolve( '../../src/templates/Challenges/ms-trophy/show.tsx' ); -const dialogue = path.resolve( - __dirname, - '../../src/templates/Challenges/dialogue/show.tsx' -); - const fillInTheBlank = path.resolve( __dirname, '../../src/templates/Challenges/fill-in-the-blank/show.tsx' @@ -77,7 +72,6 @@ const views = { odin, exam, msTrophy, - dialogue, fillInTheBlank, generic }; diff --git a/shared/config/challenge-types.ts b/shared/config/challenge-types.ts index 58af01d6de7..98ae8b921b0 100644 --- a/shared/config/challenge-types.ts +++ b/shared/config/challenge-types.ts @@ -102,7 +102,7 @@ export const viewTypes = { [msTrophy]: 'msTrophy', [multipleChoice]: 'odin', [python]: 'modern', - [dialogue]: 'dialogue', + [dialogue]: 'generic', // TODO: use generic challengeType for dialogues [fillInTheBlank]: 'fillInTheBlank', [multifilePythonCertProject]: 'classic', [generic]: 'generic'