2016-03-09 14:43:48 -08:00
|
|
|
import React, { PropTypes } from 'react';
|
|
|
|
import classnames from 'classnames';
|
|
|
|
import { connect } from 'react-redux';
|
|
|
|
import { createSelector } from 'reselect';
|
2016-03-05 21:06:04 -08:00
|
|
|
import PureComponent from 'react-pure-render/component';
|
2016-03-09 14:43:48 -08:00
|
|
|
import ReactTransitionReplace from 'react-css-transition-replace';
|
|
|
|
|
2016-06-08 18:48:30 -07:00
|
|
|
import {
|
|
|
|
goToStep,
|
|
|
|
completeAction,
|
|
|
|
submitChallenge
|
|
|
|
} from '../../redux/actions';
|
2016-05-10 13:17:57 -07:00
|
|
|
import { challengeSelector } from '../../redux/selectors';
|
2016-03-09 14:43:48 -08:00
|
|
|
import { Button, Col, Image, Row } from 'react-bootstrap';
|
|
|
|
|
2016-05-09 13:42:39 -07:00
|
|
|
const transitionTimeout = 1000;
|
2016-03-09 14:43:48 -08:00
|
|
|
const mapStateToProps = createSelector(
|
2016-05-10 13:17:57 -07:00
|
|
|
challengeSelector,
|
2016-06-08 18:48:30 -07:00
|
|
|
state => state.challengesApp.currentIndex,
|
|
|
|
state => state.challengesApp.previousIndex,
|
|
|
|
state => state.challengesApp.isActionCompleted,
|
|
|
|
(
|
|
|
|
{
|
|
|
|
challenge: { description = [] }
|
|
|
|
},
|
|
|
|
currentIndex,
|
|
|
|
previousIndex,
|
|
|
|
isActionCompleted
|
|
|
|
) => ({
|
|
|
|
currentIndex,
|
|
|
|
isActionCompleted,
|
|
|
|
step: description[currentIndex],
|
|
|
|
steps: description,
|
|
|
|
numOfSteps: description.length,
|
|
|
|
isGoingForward: currentIndex > previousIndex
|
2016-03-09 14:43:48 -08:00
|
|
|
})
|
|
|
|
);
|
|
|
|
|
|
|
|
const dispatchActions = {
|
2016-06-08 15:40:32 -07:00
|
|
|
goToStep,
|
2016-06-08 18:48:30 -07:00
|
|
|
completeAction,
|
2016-06-08 15:40:32 -07:00
|
|
|
submitChallenge
|
2016-03-09 14:43:48 -08:00
|
|
|
};
|
|
|
|
|
|
|
|
export class StepChallenge extends PureComponent {
|
2016-06-08 15:40:32 -07:00
|
|
|
constructor(...args) {
|
|
|
|
super(...args);
|
|
|
|
this.handleNextClick = this.handleNextClick.bind(this);
|
|
|
|
this.handleBackClick = this.handleBackClick.bind(this);
|
|
|
|
}
|
2016-03-09 14:43:48 -08:00
|
|
|
static displayName = 'StepChallenge';
|
|
|
|
static propTypes = {
|
2016-06-08 18:48:30 -07:00
|
|
|
currentIndex: PropTypes.number,
|
|
|
|
step: PropTypes.array,
|
|
|
|
steps: PropTypes.array,
|
|
|
|
isActionCompleted: PropTypes.bool,
|
2016-03-09 14:43:48 -08:00
|
|
|
isGoingForward: PropTypes.bool,
|
2016-06-08 18:48:30 -07:00
|
|
|
numOfSteps: PropTypes.number,
|
2016-06-08 15:40:32 -07:00
|
|
|
goToStep: PropTypes.func,
|
2016-06-08 18:48:30 -07:00
|
|
|
completeAction: PropTypes.func,
|
|
|
|
submitChallenge: PropTypes.func
|
2016-03-09 14:43:48 -08:00
|
|
|
};
|
|
|
|
|
2016-06-08 15:40:32 -07:00
|
|
|
handleNextClick() {
|
2016-06-08 18:48:30 -07:00
|
|
|
const { numOfSteps, currentIndex, submitChallenge, goToStep } = this.props;
|
|
|
|
const isLastStep = currentIndex + 1 >= numOfSteps;
|
2016-06-08 15:40:32 -07:00
|
|
|
if (isLastStep) {
|
|
|
|
return submitChallenge();
|
|
|
|
}
|
2016-06-08 18:48:30 -07:00
|
|
|
return goToStep(currentIndex + 1);
|
2016-06-08 15:40:32 -07:00
|
|
|
}
|
|
|
|
|
|
|
|
handleBackClick() {
|
2016-06-08 18:48:30 -07:00
|
|
|
const { currentIndex, goToStep } = this.props;
|
|
|
|
goToStep(currentIndex - 1);
|
2016-06-08 15:40:32 -07:00
|
|
|
}
|
|
|
|
|
2016-06-08 18:48:30 -07:00
|
|
|
renderActionButton(action, completeAction) {
|
|
|
|
const isApiAction = action === '#';
|
|
|
|
const buttonCopy = isApiAction ?
|
|
|
|
'Confirm' :
|
|
|
|
'Open link in new tab ';
|
2016-03-09 14:43:48 -08:00
|
|
|
if (!action) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<div>
|
|
|
|
<Button
|
|
|
|
block={ true }
|
|
|
|
bsSize='large'
|
|
|
|
bsStyle='primary'
|
|
|
|
href={ action }
|
2016-06-08 18:48:30 -07:00
|
|
|
onClick={ completeAction }
|
2016-06-08 15:40:32 -07:00
|
|
|
target='_blank'
|
|
|
|
>
|
2016-06-08 18:48:30 -07:00
|
|
|
{ buttonCopy } (this unlocks the next step)
|
2016-03-09 14:43:48 -08:00
|
|
|
</Button>
|
|
|
|
<div className='spacer' />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderBackButton(index) {
|
|
|
|
if (index === 0) {
|
|
|
|
return (
|
|
|
|
<Col
|
|
|
|
className='hidden-xs'
|
2016-06-08 15:40:32 -07:00
|
|
|
md={ 4 }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
{ ' ' }
|
|
|
|
</Col>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
return (
|
|
|
|
<Button
|
|
|
|
bsSize='large'
|
|
|
|
bsStyle='primary'
|
|
|
|
className='col-sm-4 col-xs-12'
|
2016-06-08 15:40:32 -07:00
|
|
|
onClick={ this.handleBackClick }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
Go to my previous step
|
|
|
|
</Button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderNextButton(hasAction, index, numOfSteps, isCompleted) {
|
|
|
|
const isLastStep = index + 1 >= numOfSteps;
|
|
|
|
const btnClass = classnames({
|
|
|
|
'col-sm-4 col-xs-12': true,
|
2016-05-09 13:42:39 -07:00
|
|
|
disabled: hasAction && !isCompleted
|
2016-03-09 14:43:48 -08:00
|
|
|
});
|
|
|
|
return (
|
|
|
|
<Button
|
|
|
|
bsSize='large'
|
|
|
|
bsStyle='primary'
|
|
|
|
className={ btnClass }
|
2016-06-08 18:48:30 -07:00
|
|
|
disabled={ hasAction && !isCompleted }
|
2016-06-08 15:40:32 -07:00
|
|
|
onClick={ this.handleNextClick }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
{ isLastStep ? 'Finish challenge' : 'Go to my next step'}
|
|
|
|
</Button>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
2016-06-08 18:48:30 -07:00
|
|
|
renderStep({
|
|
|
|
step,
|
|
|
|
currentIndex,
|
|
|
|
numOfSteps,
|
|
|
|
isActionCompleted,
|
|
|
|
completeAction
|
|
|
|
}) {
|
2016-03-09 14:43:48 -08:00
|
|
|
if (!Array.isArray(step)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
const [imgUrl, imgAlt, info, action] = step;
|
|
|
|
return (
|
|
|
|
<div
|
|
|
|
className=''
|
2016-06-08 15:40:32 -07:00
|
|
|
key={ imgUrl }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
<a href={ imgUrl }>
|
|
|
|
<Image
|
|
|
|
alt={ imgAlt }
|
|
|
|
responsive={ true }
|
2016-06-08 15:40:32 -07:00
|
|
|
src={ imgUrl }
|
|
|
|
/>
|
2016-03-09 14:43:48 -08:00
|
|
|
</a>
|
|
|
|
<Row>
|
|
|
|
<div className='spacer' />
|
|
|
|
<Col
|
|
|
|
md={ 8 }
|
|
|
|
mdOffset={ 2 }
|
|
|
|
sm={ 10 }
|
|
|
|
smOffset={ 1 }
|
2016-06-08 15:40:32 -07:00
|
|
|
xs={ 12 }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
<p
|
|
|
|
className='challenge-step-description'
|
2016-06-08 15:40:32 -07:00
|
|
|
dangerouslySetInnerHTML={{ __html: info }}
|
|
|
|
/>
|
2016-03-09 14:43:48 -08:00
|
|
|
</Col>
|
|
|
|
</Row>
|
|
|
|
<div className='spacer' />
|
|
|
|
<div className='challenge-button-block'>
|
2016-06-08 18:48:30 -07:00
|
|
|
{ this.renderActionButton(action, completeAction) }
|
|
|
|
{ this.renderBackButton(currentIndex) }
|
2016-03-09 14:43:48 -08:00
|
|
|
<Col
|
|
|
|
className='challenge-step-counter large-p text-center'
|
|
|
|
sm={ 4 }
|
2016-06-08 15:40:32 -07:00
|
|
|
xs={ 12 }
|
|
|
|
>
|
2016-06-08 18:48:30 -07:00
|
|
|
( { currentIndex + 1 } / { numOfSteps })
|
2016-03-09 14:43:48 -08:00
|
|
|
</Col>
|
|
|
|
{
|
|
|
|
this.renderNextButton(
|
|
|
|
!!action,
|
2016-06-08 18:48:30 -07:00
|
|
|
currentIndex,
|
2016-03-09 14:43:48 -08:00
|
|
|
numOfSteps,
|
2016-06-08 18:48:30 -07:00
|
|
|
isActionCompleted
|
2016-03-09 14:43:48 -08:00
|
|
|
)
|
|
|
|
}
|
|
|
|
</div>
|
|
|
|
<div className='clearfix' />
|
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|
|
|
|
|
|
|
|
renderImages(steps) {
|
|
|
|
// will preload all the image
|
|
|
|
if (!Array.isArray(steps)) {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
return steps.map(([imgUrl, imgAlt]) => (
|
|
|
|
<div key={ imgUrl }>
|
|
|
|
<Image
|
|
|
|
alt={ imgAlt }
|
|
|
|
responsive={ true }
|
2016-06-08 15:40:32 -07:00
|
|
|
src={ imgUrl }
|
|
|
|
/>
|
2016-03-09 14:43:48 -08:00
|
|
|
</div>
|
|
|
|
));
|
|
|
|
}
|
2016-03-05 21:06:04 -08:00
|
|
|
|
|
|
|
render() {
|
2016-06-08 18:48:30 -07:00
|
|
|
const { steps, isGoingForward } = this.props;
|
2016-03-09 14:43:48 -08:00
|
|
|
const transitionName = 'challenge-step-' +
|
|
|
|
(isGoingForward ? 'forward' : 'backward');
|
2016-03-05 21:06:04 -08:00
|
|
|
return (
|
2016-03-09 14:43:48 -08:00
|
|
|
<Col
|
|
|
|
md={ 8 }
|
2016-06-08 15:40:32 -07:00
|
|
|
mdOffset={ 2 }
|
|
|
|
>
|
2016-03-09 14:43:48 -08:00
|
|
|
<ReactTransitionReplace
|
|
|
|
transitionEnterTimeout={ transitionTimeout }
|
|
|
|
transitionLeaveTimeout={ transitionTimeout }
|
2016-06-08 15:40:32 -07:00
|
|
|
transitionName={ transitionName }
|
|
|
|
>
|
2016-06-08 18:48:30 -07:00
|
|
|
{ this.renderStep(this.props) }
|
2016-03-09 14:43:48 -08:00
|
|
|
</ReactTransitionReplace>
|
|
|
|
<div className='hidden'>
|
2016-06-08 18:48:30 -07:00
|
|
|
{ this.renderImages(steps) }
|
2016-03-09 14:43:48 -08:00
|
|
|
</div>
|
|
|
|
<div className='spacer' />
|
|
|
|
</Col>
|
2016-03-05 21:06:04 -08:00
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
2016-03-09 14:43:48 -08:00
|
|
|
|
|
|
|
export default connect(mapStateToProps, dispatchActions)(StepChallenge);
|