From 4096f77c39b8028055f45c936abfce58ae109a76 Mon Sep 17 00:00:00 2001 From: Berkeley Martinez Date: Sat, 9 Jan 2016 17:00:49 -0800 Subject: [PATCH] Use release to indicate answer attempt --- .../app/routes/Hikes/components/Questions.jsx | 99 +++++----- common/app/routes/Hikes/flux/Actions.js | 170 +++++++++++------- 2 files changed, 150 insertions(+), 119 deletions(-) diff --git a/common/app/routes/Hikes/components/Questions.jsx b/common/app/routes/Hikes/components/Questions.jsx index 1a7b08c9e3..38dade4633 100644 --- a/common/app/routes/Hikes/components/Questions.jsx +++ b/common/app/routes/Hikes/components/Questions.jsx @@ -9,7 +9,7 @@ import { Row } from 'react-bootstrap'; -const ANSWER_THRESHOLD = 200; +const answerThreshold = 200; export default contain( { @@ -35,7 +35,7 @@ export default contain( isPressed, showInfo, shake, - username + isSignedIn: !!username }; } }, @@ -51,56 +51,46 @@ export default contain( isPressed: PropTypes.bool, showInfo: PropTypes.bool, shake: PropTypes.bool, - username: PropTypes.string, + isSignedIn: PropTypes.bool, hikesActions: PropTypes.object }, - handleMouseDown({ pageX, pageY, touches }) { - if (touches) { - ({ pageX, pageY } = touches[0]); - } - const { mouse: [pressX, pressY], hikesActions } = this.props; - hikesActions.grabQuestion({ pressX, pressY, pageX, pageY }); - }, - - handleMouseUp() { + handleMouseUp(e, answer) { + e.stopPropagation(); if (!this.props.isPressed) { return null; } + + const { + hike, + currentQuestion, + isSignedIn, + delta + } = this.props; + this.props.hikesActions.releaseQuestion(); + this.props.hikesActions.answer({ + e, + answer, + hike, + delta, + currentQuestion, + isSignedIn, + threshold: answerThreshold + }); }, - handleMouseMove(answer) { + handleMouseMove(e) { if (!this.props.isPressed) { - return () => {}; + return null; } + const { delta, hikesActions } = this.props; - return (e) => { - let { pageX, pageY, touches } = e; - - if (touches) { - e.preventDefault(); - // these re-assigns the values of pageX, pageY from touches - ({ pageX, pageY } = touches[0]); - } - - const { delta: [dx, dy], hikesActions } = this.props; - const mouse = [pageX - dx, pageY - dy]; - - if (mouse[0] >= ANSWER_THRESHOLD) { - return this.onAnswer(answer, true)(); - } - - if (mouse[0] <= -ANSWER_THRESHOLD) { - return this.onAnswer(answer, false)(); - } - - return hikesActions.moveQuestion(mouse); - }; + hikesActions.moveQuestion({ e, delta }); }, onAnswer(answer, userAnswer) { - const { hikesActions } = this.props; + const { isSignedIn, hike, hikesActions } = this.props; return (e) => { if (e && e.preventDefault) { e.preventDefault(); @@ -109,20 +99,12 @@ export default contain( return hikesActions.answer({ answer, userAnswer, - props: this.props + hike, + isSignedIn }); }; }, - routerWillLeave(nextState, router, cb) { - // TODO(berks): do animated transitions here stuff here - this.setState({ - showInfo: false, - isCorrect: false, - mouse: [0, 0] - }, cb); - }, - renderInfo(showInfo, info, hideInfo) { if (!info) { return null; @@ -150,6 +132,8 @@ export default contain( }, renderQuestion(number, question, answer, shake) { + const { hikesActions } = this.props; + const mouseUp = e => this.handleMouseUp(e, answer); return ({ x }) => { const style = { WebkitTransform: `translate3d(${ x }px, 0, 0)`, @@ -160,13 +144,13 @@ export default contain(

{ question }

@@ -175,19 +159,20 @@ export default contain( }, render() { - const { showInfo, shake } = this.props; const { hike: { tests = [] } = {}, mouse: [x], currentQuestion, - hikesActions + hikesActions, + showInfo, + shake } = this.props; const [ question, answer, info ] = tests[currentQuestion - 1] || []; return ( this.handleMouseUp(e, answer) } xs={ 8 } xsOffset={ 2 }> diff --git a/common/app/routes/Hikes/flux/Actions.js b/common/app/routes/Hikes/flux/Actions.js index 5220286759..25c879d302 100644 --- a/common/app/routes/Hikes/flux/Actions.js +++ b/common/app/routes/Hikes/flux/Actions.js @@ -4,6 +4,7 @@ import { Actions } from 'thundercats'; import debugFactory from 'debug'; const debug = debugFactory('freecc:hikes:actions'); +const noOp = { transform: () => {} }; function getCurrentHike(hikes = [{}], dashedName, currentHike) { if (!dashedName) { @@ -35,18 +36,17 @@ function findNextHike(hikes, id) { return hikes[currentIndex + 1] || hikes[0]; } -function releaseQuestion(state) { - const oldHikesApp = state.hikesApp; - const hikesApp = { - ...oldHikesApp, - isPressed: false, - delta: [0, 0], - mouse: oldHikesApp.isCorrect ? - oldHikesApp.mouse : - [0, 0] - }; - return { ...state, hikesApp }; +function getMouse(e, [dx, dy]) { + let { pageX, pageY, touches } = e; + + if (touches) { + e.preventDefault(); + // these re-assigns the values of pageX, pageY from touches + ({ pageX, pageY } = touches[0]); + } + + return [pageX - dx, pageY - dy]; } export default Actions({ @@ -107,64 +107,115 @@ export default Actions({ }; }, - grabQuestion({ pressX, pressY, pageX, pageY }) { - const dx = pageX - pressX; - const dy = pageY - pressY; - - const delta = [dx, dy]; - const mouse = [pageX - dx, pageY - dy]; + grabQuestion(e) { + const { pageX, pageY } = e; + const delta = [pageX, pageY]; + const mouse = getMouse(e, delta); return { transform(state) { - const hikesApp = { ...state.hikesApp, isPressed: true, delta, mouse }; - return { ...state, hikesApp }; + return { + ...state, + hikesApp: { + ...state.hikesApp, + isPressed: true, + delta, + mouse + } + }; } }; }, releaseQuestion() { - return { transform: releaseQuestion }; - }, - - moveQuestion(mouse) { return { transform(state) { - const hikesApp = { ...state.hikesApp, mouse }; - return { ...state, hikesApp }; + return { + ...state, + hikesApp: { + ...state.hikesApp, + isPressed: false, + mouse: [0, 0] + } + }; + } + }; + }, + + moveQuestion({ e, delta }) { + const mouse = getMouse(e, delta); + + return { + transform(state) { + return { + ...state, + hikesApp: { + ...state.hikesApp, + mouse + } + }; } }; }, answer({ + e, answer, userAnswer, - props: { - hike: { id, name, tests, challengeType }, - currentQuestion, - username - } + hike: { id, name, tests, challengeType }, + currentQuestion, + isSignedIn, + delta, + threshold }) { + if (typeof userAnswer === 'undefined') { + const [positionX] = getMouse(e, delta); + + // question released under threshold + if (Math.abs(positionX) < threshold) { + return noOp; + } + + if (positionX >= threshold) { + userAnswer = true; + } + + if (positionX <= -threshold) { + userAnswer = false; + } + } // incorrect question if (answer !== userAnswer) { const startShake = { transform(state) { - const hikesApp = { ...state.hikesApp, showInfo: true, shake: true }; - return { ...state, hikesApp }; + return { + ...state, + hikesApp: { + ...state.hikesApp, + showInfo: true, + shake: true + } + }; } }; const removeShake = { transform(state) { - const hikesApp = { ...state.hikesApp, shake: false }; - return { ...state, hikesApp }; + return { + ...state, + hikesApp: { + ...state.hikesApp, + shake: false + } + }; } }; return Observable .just(removeShake) .delay(500) - .startWith({ transform: releaseQuestion }, startShake); + .startWith(startShake); } // move to next question @@ -198,22 +249,21 @@ export default Actions({ } // challenge completed - const optimisticSave = username ? + const optimisticSave = isSignedIn ? this.post$('/completed-challenge', { id, name, challengeType }) : Observable.just(true); const correctAnswer = { transform(state) { - const hikesApp = { - ...state.hikesApp, - isCorrect: true, - isPressed: false, - delta: [0, 0], - mouse: [ userAnswer ? 1000 : -1000, 0] - }; return { ...state, - hikesApp + hikesApp: { + ...state.hikesApp, + isCorrect: true, + isPressed: false, + delta: [0, 0], + mouse: [ userAnswer ? 1000 : -1000, 0] + } }; } }; @@ -223,26 +273,16 @@ export default Actions({ const { hikes, currentHike: { id } } = state.hikesApp; const currentHike = findNextHike(hikes, id); - // go to next route - state.location = { - action: 'PUSH', - pathname: currentHike && currentHike.dashedName ? - `/hikes/${ currentHike.dashedName }` : - '/hikes' - }; - - const hikesApp = { - ...state.hikesApp, - currentHike, - showQuestions: false, - currentQuestion: 1, - mouse: [0, 0] - }; - return { ...state, - points: username ? state.points + 1 : state.points, - hikesApp, + points: isSignedIn ? state.points + 1 : state.points, + hikesApp: { + ...state.hikesApp, + currentHike, + showQuestions: false, + currentQuestion: 1, + mouse: [0, 0] + }, toast: { title: 'Congratulations!', message: 'Hike completed', @@ -250,6 +290,12 @@ export default Actions({ state.toast.id + 1 : 0, type: 'success' + }, + location: { + action: 'PUSH', + pathname: currentHike && currentHike.dashedName ? + `/hikes/${ currentHike.dashedName }` : + '/hikes' } }; },