Use release to indicate answer attempt

This commit is contained in:
Berkeley Martinez
2016-01-09 17:00:49 -08:00
parent 7da7f7c47a
commit 4096f77c39
2 changed files with 150 additions and 119 deletions

View File

@ -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(
<Panel
className={ shake ? 'animated swing shake' : '' }
header={ title }
onMouseDown={ this.handleMouseDown }
onMouseLeave={ this.handleMouseUp }
onMouseMove={ this.handleMouseMove(answer) }
onMouseUp={ this.handleMouseUp }
onTouchEnd={ this.handleMouseUp }
onTouchMove={ this.handleMouseMove(answer) }
onTouchStart={ this.handleMouseDown }
onMouseDown={ hikesActions.grabQuestion }
onMouseLeave={ mouseUp }
onMouseMove={ this.handleMouseMove }
onMouseUp={ mouseUp }
onTouchEnd={ mouseUp }
onTouchMove={ this.handleMouseMove }
onTouchStart={ hikesActions.grabQuestion }
style={ style }>
<p>{ question }</p>
</Panel>
@ -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 (
<Col
onMouseUp={ this.handleMouseUp }
onMouseUp={ e => this.handleMouseUp(e, answer) }
xs={ 8 }
xsOffset={ 2 }>
<Row>

View File

@ -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'
}
};
},