Files
freeCodeCamp/client/src/templates/Challenges/components/CompletionModal.js

171 lines
4.1 KiB
JavaScript
Raw Normal View History

import React, { Component } from 'react';
2018-04-06 14:51:52 +01:00
import PropTypes from 'prop-types';
import noop from 'lodash/noop';
import { connect } from 'react-redux';
import { createSelector } from 'reselect';
import { Button, Modal } from '@freecodecamp/react-bootstrap';
2018-04-06 14:51:52 +01:00
import ga from '../../../analytics';
import GreenPass from './icons/GreenPass';
2018-04-06 14:51:52 +01:00
import { dasherize } from '../../../../utils';
import './completion-modal.css';
2018-04-06 14:51:52 +01:00
import {
closeModal,
submitChallenge,
isCompletionModalOpenSelector,
successMessageSelector,
challengeFilesSelector,
challengeMetaSelector
} from '../redux';
2018-04-06 14:51:52 +01:00
const mapStateToProps = createSelector(
challengeFilesSelector,
challengeMetaSelector,
2018-04-06 14:51:52 +01:00
isCompletionModalOpenSelector,
successMessageSelector,
(files, { title }, isOpen, message) => ({
files,
title,
2018-04-06 14:51:52 +01:00
isOpen,
message
})
);
const mapDispatchToProps = function(dispatch) {
const dispatchers = {
close: () => dispatch(closeModal('completion')),
handleKeypress: e => {
if (e.keyCode === 13 && (e.ctrlKey || e.metaKey)) {
e.preventDefault();
2018-04-06 14:51:52 +01:00
dispatch(submitChallenge());
}
},
submitChallenge: () => {
dispatch(submitChallenge());
}
};
return () => dispatchers;
};
const propTypes = {
close: PropTypes.func.isRequired,
files: PropTypes.object.isRequired,
2018-04-06 14:51:52 +01:00
handleKeypress: PropTypes.func.isRequired,
isOpen: PropTypes.bool,
message: PropTypes.string,
submitChallenge: PropTypes.func.isRequired,
title: PropTypes.string
2018-04-06 14:51:52 +01:00
};
export class CompletionModal extends Component {
state = {
downloadURL: null
};
static getDerivedStateFromProps(props, state) {
const { files, isOpen } = props;
if (!isOpen) {
return null;
}
const { downloadURL } = state;
if (downloadURL) {
URL.revokeObjectURL(downloadURL);
}
let newURL = null;
if (Object.keys(files).length) {
const filesForDownload = Object.keys(files)
.map(key => files[key])
.reduce(
(allFiles, { path, contents }) => ({
...allFiles,
[path]: contents
}),
{}
);
const blob = new Blob([JSON.stringify(filesForDownload, null, 2)], {
type: 'text/json'
});
newURL = URL.createObjectURL(blob);
}
return { downloadURL: newURL };
}
componentWillUnmount() {
if (this.state.downloadURL) {
URL.revokeObjectURL(this.state.downloadURL);
}
}
2018-04-06 14:51:52 +01:00
render() {
const {
close,
isOpen,
submitChallenge,
handleKeypress,
message,
title
2018-04-06 14:51:52 +01:00
} = this.props;
if (isOpen) {
ga.modalview('/completion-modal');
}
const dashedName = dasherize(title);
2018-04-06 14:51:52 +01:00
return (
<Modal
animation={false}
bsSize='lg'
dialogClassName='challenge-success-modal'
2018-04-06 14:51:52 +01:00
keyboard={true}
onHide={close}
onKeyDown={isOpen ? handleKeypress : noop}
show={isOpen}
>
<Modal.Header
className='challenge-list-header fcc-modal'
closeButton={true}
>
<Modal.Title className='text-center'>{message}</Modal.Title>
2018-04-06 14:51:52 +01:00
</Modal.Header>
<Modal.Body className='completion-modal-body'>
<div className='success-icon-wrapper'>
<GreenPass />
2018-04-06 14:51:52 +01:00
</div>
</Modal.Body>
<Modal.Footer>
<Button
block={true}
bsSize='large'
bsStyle='primary'
onClick={submitChallenge}
>
Submit and go to next challenge{' '}
<span className='hidden-xs'>(Ctrl + Enter)</span>
2018-04-06 14:51:52 +01:00
</Button>
{this.state.downloadURL ? (
2018-09-13 22:09:12 +03:00
<Button
block={true}
bsSize='lg'
bsStyle='primary'
2019-02-15 19:38:25 +03:00
className='btn-invert'
2018-09-13 22:09:12 +03:00
download={`${dashedName}.json`}
href={this.state.downloadURL}
>
2018-09-13 22:09:12 +03:00
Download my solution
</Button>
) : null}
2018-04-06 14:51:52 +01:00
</Modal.Footer>
</Modal>
);
}
}
CompletionModal.displayName = 'CompletionModal';
CompletionModal.propTypes = propTypes;
export default connect(
mapStateToProps,
mapDispatchToProps
)(CompletionModal);