import React, { PropTypes } from 'react'; import { Spring } from 'react-motion'; import { History, Lifecycle } from 'react-router'; import debugFactory from 'debug'; import { Button, Col, Modal, Panel, Row } from 'react-bootstrap'; import { postJSON$ } from '../../../../utils/ajax-stream.js'; const debug = debugFactory('freecc:hikes'); const ANSWER_THRESHOLD = 200; export default React.createClass({ displayName: 'Question', mixins: [ History, Lifecycle ], propTypes: { currentHike: PropTypes.object, dashedName: PropTypes.string, hikes: PropTypes.array, params: PropTypes.object }, getInitialState: () => ({ mouse: [0, 0], correct: false, delta: [0, 0], isPressed: false, showInfo: false, shake: false }), getTweenValues() { const { mouse: [x, y] } = this.state; return { val: { x, y }, config: [120, 10] }; }, handleMouseDown({ pageX, pageY, touches }) { if (touches) { ({ pageX, pageY } = touches[0]); } const { mouse: [pressX, pressY] } = this.state; const dx = pageX - pressX; const dy = pageY - pressY; this.setState({ isPressed: true, delta: [dx, dy], mouse: [pageX - dx, pageY - dy] }); }, handleMouseUp() { const { correct } = this.state; if (correct) { return this.setState({ isPressed: false, delta: [0, 0] }); } this.setState({ isPressed: false, mouse: [0, 0], delta: [0, 0] }); }, handleMouseMove(answer) { return (e) => { let { pageX, pageY, touches } = e; if (touches) { e.preventDefault(); // these reassins the values of pageX, pageY from touches ({ pageX, pageY } = touches[0]); } const { isPressed, delta: [dx, dy] } = this.state; if (isPressed) { const mouse = [pageX - dx, pageY - dy]; if (mouse[0] >= ANSWER_THRESHOLD) { this.handleMouseUp(); return this.onAnswer(answer, true)(); } if (mouse[0] <= -ANSWER_THRESHOLD) { this.handleMouseUp(); return this.onAnswer(answer, false)(); } this.setState({ mouse }); } }; }, hideInfo() { this.setState({ showInfo: false }); }, onAnswer(answer, userAnswer) { return (e) => { if (e && e.preventDefault) { e.preventDefault(); } if (this.disposeTimeout) { clearTimeout(this.disposeTimeout); this.disposeTimeout = null; } if (answer === userAnswer) { debug('correct answer!'); this.setState({ correct: true, mouse: [ userAnswer ? 1000 : -1000, 0] }); this.disposeTimeout = setTimeout(() => { this.onCorrectAnswer(); }, 1000); return; } debug('incorrect'); this.setState({ showInfo: true, shake: true }); this.disposeTimeout = setTimeout( () => this.setState({ shake: false }), 500 ); }; }, onCorrectAnswer() { const { hikes, currentHike } = this.props; const { dashedName, number } = this.props.params; const { id, name, difficulty, tests } = currentHike; const nextQuestionIndex = +number; postJSON$('/completed-challenge', { id, name }).subscribeOnCompleted(() => { if (tests[nextQuestionIndex]) { return this.history.pushState( null, `/hikes/${ dashedName }/questions/${ nextQuestionIndex + 1 }` ); } // next questions does not exist; debug('finding next hike'); const nextHike = [].slice.call(hikes) // hikes is in oder of difficulty, lets get reverse order .reverse() // now lets find the hike with the difficulty right above this one .reduce((lowerHike, hike) => { if (hike.difficulty > difficulty) { return hike; } return lowerHike; }, null); if (nextHike) { return this.history.pushState(null, `/hikes/${ nextHike.dashedName }`); } debug( 'next Hike was not found, currentHike %s', currentHike.dashedName ); this.history.pushState(null, '/hikes'); }); }, routerWillLeave(nextState, router, cb) { // TODO(berks): do animated transitions here stuff here this.setState({ showInfo: false, correct: false, mouse: [0, 0] }, cb); }, renderInfo(showInfo, info) { if (!info) { return null; } return (

{ info }

); }, renderQuestion(number, question, answer, shake) { return ({ val: { x } }) => { const style = { WebkitTransform: `translate3d(${ x }px, 0, 0)`, transform: `translate3d(${ x }px, 0, 0)` }; const title =

Question { number }

; return (

{ question }

); }; }, render() { const { showInfo, shake } = this.state; const { currentHike: { tests = [] } } = this.props; const { number = '1' } = this.props.params; const [question, answer, info] = tests[number - 1] || []; return ( { this.renderQuestion(number, question, answer, shake) } { this.renderInfo(showInfo, info) } ); } });