diff --git a/client/package-lock.json b/client/package-lock.json index 15bfc69d26..c0f90f22c0 100644 --- a/client/package-lock.json +++ b/client/package-lock.json @@ -4074,6 +4074,11 @@ "resolved": "https://registry.npmjs.org/better-queue-memory/-/better-queue-memory-1.0.4.tgz", "integrity": "sha512-SWg5wFIShYffEmJpI6LgbL8/3Dqhku7xI1oEiy6FroP9DbcZlG0ZDjxvPdP9t7hTGW40IpIcC6zVoGT1oxjOuA==" }, + "bezier-easing": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/bezier-easing/-/bezier-easing-2.1.0.tgz", + "integrity": "sha1-wE3+i5JtbsrKGBPWn/F5t8ICXYY=" + }, "big.js": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/big.js/-/big.js-3.2.0.tgz", diff --git a/client/package.json b/client/package.json index da0041e8f4..e08641d2e9 100644 --- a/client/package.json +++ b/client/package.json @@ -18,6 +18,7 @@ "@reach/router": "^1.2.1", "algoliasearch": "^3.35.1", "axios": "^0.19.0", + "bezier-easing": "^2.1.0", "browser-cookies": "^1.2.0", "chai": "^4.2.0", "date-fns": "^1.30.1", diff --git a/client/src/templates/Challenges/classic/Show.js b/client/src/templates/Challenges/classic/Show.js index 6c8886ff8a..18620dd494 100644 --- a/client/src/templates/Challenges/classic/Show.js +++ b/client/src/templates/Challenges/classic/Show.js @@ -252,7 +252,11 @@ class ShowClassic extends Component { } render() { - const { forumTopicId, title } = this.getChallenge(); + const { + fields: { blockName }, + forumTopicId, + title + } = this.getChallenge(); const { executeChallenge, pageContext: { @@ -298,7 +302,7 @@ class ShowClassic extends Component { testOutput={this.renderTestOutput()} /> - + diff --git a/client/src/templates/Challenges/components/CompletionModal.js b/client/src/templates/Challenges/components/CompletionModal.js index c5794dcf10..91d77ff5f4 100644 --- a/client/src/templates/Challenges/components/CompletionModal.js +++ b/client/src/templates/Challenges/components/CompletionModal.js @@ -4,10 +4,11 @@ import noop from 'lodash/noop'; import { connect } from 'react-redux'; import { createSelector } from 'reselect'; import { Button, Modal } from '@freecodecamp/react-bootstrap'; +import { useStaticQuery, graphql } from 'gatsby'; import ga from '../../../analytics'; import Login from '../../../components/Header/components/Login'; -import GreenPass from '../../../assets/icons/GreenPass'; +import CompletionModalBody from './CompletionModalBody'; import { dasherize } from '../../../../../utils/slugs'; @@ -16,6 +17,7 @@ import './completion-modal.css'; import { closeModal, submitChallenge, + completedChallengesIds, isCompletionModalOpenSelector, successMessageSelector, challengeFilesSelector, @@ -27,12 +29,22 @@ import { isSignedInSelector } from '../../../redux'; const mapStateToProps = createSelector( challengeFilesSelector, challengeMetaSelector, + completedChallengesIds, isCompletionModalOpenSelector, isSignedInSelector, successMessageSelector, - (files, { title }, isOpen, isSignedIn, message) => ({ + ( + files, + { title, id }, + completedChallengesIds, + isOpen, + isSignedIn, + message + ) => ({ files, title, + id, + completedChallengesIds, isOpen, isSignedIn, message @@ -59,9 +71,13 @@ const mapDispatchToProps = function(dispatch) { }; const propTypes = { + blockName: PropTypes.string, close: PropTypes.func.isRequired, + completedChallengesIds: PropTypes.array, + currentBlockIds: PropTypes.array, files: PropTypes.object.isRequired, handleKeypress: PropTypes.func.isRequired, + id: PropTypes.string, isOpen: PropTypes.bool, isSignedIn: PropTypes.bool.isRequired, message: PropTypes.string, @@ -69,7 +85,27 @@ const propTypes = { title: PropTypes.string }; -export class CompletionModal extends Component { +export function getCompletedPercent( + completedChallengesIds, + currentBlockIds, + currentChallengeId +) { + completedChallengesIds = completedChallengesIds.includes(currentChallengeId) + ? completedChallengesIds + : [...completedChallengesIds, currentChallengeId]; + + const completedChallengesInBlock = completedChallengesIds.filter(id => { + return currentBlockIds.includes(id); + }); + + const completedPercent = Math.round( + (completedChallengesInBlock.length / currentBlockIds.length) * 100 + ); + + return completedPercent > 100 ? 100 : completedPercent; +} + +export class CompletionModalInner extends Component { state = { downloadURL: null }; @@ -111,7 +147,11 @@ export class CompletionModal extends Component { render() { const { + blockName = '', close, + completedChallengesIds = [], + currentBlockIds = [], + id = '', isOpen, isSignedIn, submitChallenge, @@ -119,6 +159,11 @@ export class CompletionModal extends Component { message, title } = this.props; + + const completedPercent = !isSignedIn + ? 0 + : getCompletedPercent(completedChallengesIds, currentBlockIds, id); + if (isOpen) { ga.modalview('/completion-modal'); } @@ -137,12 +182,13 @@ export class CompletionModal extends Component { className='challenge-list-header fcc-modal' closeButton={true} > - {message} + {message} -
- -
+