Use release to indicate answer attempt
This commit is contained in:
@ -9,7 +9,7 @@ import {
|
|||||||
Row
|
Row
|
||||||
} from 'react-bootstrap';
|
} from 'react-bootstrap';
|
||||||
|
|
||||||
const ANSWER_THRESHOLD = 200;
|
const answerThreshold = 200;
|
||||||
|
|
||||||
export default contain(
|
export default contain(
|
||||||
{
|
{
|
||||||
@ -35,7 +35,7 @@ export default contain(
|
|||||||
isPressed,
|
isPressed,
|
||||||
showInfo,
|
showInfo,
|
||||||
shake,
|
shake,
|
||||||
username
|
isSignedIn: !!username
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@ -51,56 +51,46 @@ export default contain(
|
|||||||
isPressed: PropTypes.bool,
|
isPressed: PropTypes.bool,
|
||||||
showInfo: PropTypes.bool,
|
showInfo: PropTypes.bool,
|
||||||
shake: PropTypes.bool,
|
shake: PropTypes.bool,
|
||||||
username: PropTypes.string,
|
isSignedIn: PropTypes.bool,
|
||||||
hikesActions: PropTypes.object
|
hikesActions: PropTypes.object
|
||||||
},
|
},
|
||||||
|
|
||||||
handleMouseDown({ pageX, pageY, touches }) {
|
handleMouseUp(e, answer) {
|
||||||
if (touches) {
|
e.stopPropagation();
|
||||||
({ pageX, pageY } = touches[0]);
|
|
||||||
}
|
|
||||||
const { mouse: [pressX, pressY], hikesActions } = this.props;
|
|
||||||
hikesActions.grabQuestion({ pressX, pressY, pageX, pageY });
|
|
||||||
},
|
|
||||||
|
|
||||||
handleMouseUp() {
|
|
||||||
if (!this.props.isPressed) {
|
if (!this.props.isPressed) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const {
|
||||||
|
hike,
|
||||||
|
currentQuestion,
|
||||||
|
isSignedIn,
|
||||||
|
delta
|
||||||
|
} = this.props;
|
||||||
|
|
||||||
this.props.hikesActions.releaseQuestion();
|
this.props.hikesActions.releaseQuestion();
|
||||||
|
this.props.hikesActions.answer({
|
||||||
|
e,
|
||||||
|
answer,
|
||||||
|
hike,
|
||||||
|
delta,
|
||||||
|
currentQuestion,
|
||||||
|
isSignedIn,
|
||||||
|
threshold: answerThreshold
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
handleMouseMove(answer) {
|
handleMouseMove(e) {
|
||||||
if (!this.props.isPressed) {
|
if (!this.props.isPressed) {
|
||||||
return () => {};
|
return null;
|
||||||
}
|
}
|
||||||
|
const { delta, hikesActions } = this.props;
|
||||||
|
|
||||||
return (e) => {
|
hikesActions.moveQuestion({ e, delta });
|
||||||
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);
|
|
||||||
};
|
|
||||||
},
|
},
|
||||||
|
|
||||||
onAnswer(answer, userAnswer) {
|
onAnswer(answer, userAnswer) {
|
||||||
const { hikesActions } = this.props;
|
const { isSignedIn, hike, hikesActions } = this.props;
|
||||||
return (e) => {
|
return (e) => {
|
||||||
if (e && e.preventDefault) {
|
if (e && e.preventDefault) {
|
||||||
e.preventDefault();
|
e.preventDefault();
|
||||||
@ -109,20 +99,12 @@ export default contain(
|
|||||||
return hikesActions.answer({
|
return hikesActions.answer({
|
||||||
answer,
|
answer,
|
||||||
userAnswer,
|
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) {
|
renderInfo(showInfo, info, hideInfo) {
|
||||||
if (!info) {
|
if (!info) {
|
||||||
return null;
|
return null;
|
||||||
@ -150,6 +132,8 @@ export default contain(
|
|||||||
},
|
},
|
||||||
|
|
||||||
renderQuestion(number, question, answer, shake) {
|
renderQuestion(number, question, answer, shake) {
|
||||||
|
const { hikesActions } = this.props;
|
||||||
|
const mouseUp = e => this.handleMouseUp(e, answer);
|
||||||
return ({ x }) => {
|
return ({ x }) => {
|
||||||
const style = {
|
const style = {
|
||||||
WebkitTransform: `translate3d(${ x }px, 0, 0)`,
|
WebkitTransform: `translate3d(${ x }px, 0, 0)`,
|
||||||
@ -160,13 +144,13 @@ export default contain(
|
|||||||
<Panel
|
<Panel
|
||||||
className={ shake ? 'animated swing shake' : '' }
|
className={ shake ? 'animated swing shake' : '' }
|
||||||
header={ title }
|
header={ title }
|
||||||
onMouseDown={ this.handleMouseDown }
|
onMouseDown={ hikesActions.grabQuestion }
|
||||||
onMouseLeave={ this.handleMouseUp }
|
onMouseLeave={ mouseUp }
|
||||||
onMouseMove={ this.handleMouseMove(answer) }
|
onMouseMove={ this.handleMouseMove }
|
||||||
onMouseUp={ this.handleMouseUp }
|
onMouseUp={ mouseUp }
|
||||||
onTouchEnd={ this.handleMouseUp }
|
onTouchEnd={ mouseUp }
|
||||||
onTouchMove={ this.handleMouseMove(answer) }
|
onTouchMove={ this.handleMouseMove }
|
||||||
onTouchStart={ this.handleMouseDown }
|
onTouchStart={ hikesActions.grabQuestion }
|
||||||
style={ style }>
|
style={ style }>
|
||||||
<p>{ question }</p>
|
<p>{ question }</p>
|
||||||
</Panel>
|
</Panel>
|
||||||
@ -175,19 +159,20 @@ export default contain(
|
|||||||
},
|
},
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { showInfo, shake } = this.props;
|
|
||||||
const {
|
const {
|
||||||
hike: { tests = [] } = {},
|
hike: { tests = [] } = {},
|
||||||
mouse: [x],
|
mouse: [x],
|
||||||
currentQuestion,
|
currentQuestion,
|
||||||
hikesActions
|
hikesActions,
|
||||||
|
showInfo,
|
||||||
|
shake
|
||||||
} = this.props;
|
} = this.props;
|
||||||
|
|
||||||
const [ question, answer, info ] = tests[currentQuestion - 1] || [];
|
const [ question, answer, info ] = tests[currentQuestion - 1] || [];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Col
|
<Col
|
||||||
onMouseUp={ this.handleMouseUp }
|
onMouseUp={ e => this.handleMouseUp(e, answer) }
|
||||||
xs={ 8 }
|
xs={ 8 }
|
||||||
xsOffset={ 2 }>
|
xsOffset={ 2 }>
|
||||||
<Row>
|
<Row>
|
||||||
|
@ -4,6 +4,7 @@ import { Actions } from 'thundercats';
|
|||||||
import debugFactory from 'debug';
|
import debugFactory from 'debug';
|
||||||
|
|
||||||
const debug = debugFactory('freecc:hikes:actions');
|
const debug = debugFactory('freecc:hikes:actions');
|
||||||
|
const noOp = { transform: () => {} };
|
||||||
|
|
||||||
function getCurrentHike(hikes = [{}], dashedName, currentHike) {
|
function getCurrentHike(hikes = [{}], dashedName, currentHike) {
|
||||||
if (!dashedName) {
|
if (!dashedName) {
|
||||||
@ -35,18 +36,17 @@ function findNextHike(hikes, id) {
|
|||||||
return hikes[currentIndex + 1] || hikes[0];
|
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({
|
export default Actions({
|
||||||
@ -107,64 +107,115 @@ export default Actions({
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
grabQuestion({ pressX, pressY, pageX, pageY }) {
|
grabQuestion(e) {
|
||||||
const dx = pageX - pressX;
|
const { pageX, pageY } = e;
|
||||||
const dy = pageY - pressY;
|
const delta = [pageX, pageY];
|
||||||
|
const mouse = getMouse(e, delta);
|
||||||
const delta = [dx, dy];
|
|
||||||
const mouse = [pageX - dx, pageY - dy];
|
|
||||||
|
|
||||||
return {
|
return {
|
||||||
transform(state) {
|
transform(state) {
|
||||||
const hikesApp = { ...state.hikesApp, isPressed: true, delta, mouse };
|
return {
|
||||||
return { ...state, hikesApp };
|
...state,
|
||||||
|
hikesApp: {
|
||||||
|
...state.hikesApp,
|
||||||
|
isPressed: true,
|
||||||
|
delta,
|
||||||
|
mouse
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
|
||||||
releaseQuestion() {
|
releaseQuestion() {
|
||||||
return { transform: releaseQuestion };
|
|
||||||
},
|
|
||||||
|
|
||||||
moveQuestion(mouse) {
|
|
||||||
return {
|
return {
|
||||||
transform(state) {
|
transform(state) {
|
||||||
const hikesApp = { ...state.hikesApp, mouse };
|
return {
|
||||||
return { ...state, hikesApp };
|
...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({
|
answer({
|
||||||
|
e,
|
||||||
answer,
|
answer,
|
||||||
userAnswer,
|
userAnswer,
|
||||||
props: {
|
hike: { id, name, tests, challengeType },
|
||||||
hike: { id, name, tests, challengeType },
|
currentQuestion,
|
||||||
currentQuestion,
|
isSignedIn,
|
||||||
username
|
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
|
// incorrect question
|
||||||
if (answer !== userAnswer) {
|
if (answer !== userAnswer) {
|
||||||
const startShake = {
|
const startShake = {
|
||||||
transform(state) {
|
transform(state) {
|
||||||
const hikesApp = { ...state.hikesApp, showInfo: true, shake: true };
|
return {
|
||||||
return { ...state, hikesApp };
|
...state,
|
||||||
|
hikesApp: {
|
||||||
|
...state.hikesApp,
|
||||||
|
showInfo: true,
|
||||||
|
shake: true
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const removeShake = {
|
const removeShake = {
|
||||||
transform(state) {
|
transform(state) {
|
||||||
const hikesApp = { ...state.hikesApp, shake: false };
|
return {
|
||||||
return { ...state, hikesApp };
|
...state,
|
||||||
|
hikesApp: {
|
||||||
|
...state.hikesApp,
|
||||||
|
shake: false
|
||||||
|
}
|
||||||
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
return Observable
|
return Observable
|
||||||
.just(removeShake)
|
.just(removeShake)
|
||||||
.delay(500)
|
.delay(500)
|
||||||
.startWith({ transform: releaseQuestion }, startShake);
|
.startWith(startShake);
|
||||||
}
|
}
|
||||||
|
|
||||||
// move to next question
|
// move to next question
|
||||||
@ -198,22 +249,21 @@ export default Actions({
|
|||||||
}
|
}
|
||||||
|
|
||||||
// challenge completed
|
// challenge completed
|
||||||
const optimisticSave = username ?
|
const optimisticSave = isSignedIn ?
|
||||||
this.post$('/completed-challenge', { id, name, challengeType }) :
|
this.post$('/completed-challenge', { id, name, challengeType }) :
|
||||||
Observable.just(true);
|
Observable.just(true);
|
||||||
|
|
||||||
const correctAnswer = {
|
const correctAnswer = {
|
||||||
transform(state) {
|
transform(state) {
|
||||||
const hikesApp = {
|
|
||||||
...state.hikesApp,
|
|
||||||
isCorrect: true,
|
|
||||||
isPressed: false,
|
|
||||||
delta: [0, 0],
|
|
||||||
mouse: [ userAnswer ? 1000 : -1000, 0]
|
|
||||||
};
|
|
||||||
return {
|
return {
|
||||||
...state,
|
...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 { hikes, currentHike: { id } } = state.hikesApp;
|
||||||
const currentHike = findNextHike(hikes, id);
|
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 {
|
return {
|
||||||
...state,
|
...state,
|
||||||
points: username ? state.points + 1 : state.points,
|
points: isSignedIn ? state.points + 1 : state.points,
|
||||||
hikesApp,
|
hikesApp: {
|
||||||
|
...state.hikesApp,
|
||||||
|
currentHike,
|
||||||
|
showQuestions: false,
|
||||||
|
currentQuestion: 1,
|
||||||
|
mouse: [0, 0]
|
||||||
|
},
|
||||||
toast: {
|
toast: {
|
||||||
title: 'Congratulations!',
|
title: 'Congratulations!',
|
||||||
message: 'Hike completed',
|
message: 'Hike completed',
|
||||||
@ -250,6 +290,12 @@ export default Actions({
|
|||||||
state.toast.id + 1 :
|
state.toast.id + 1 :
|
||||||
0,
|
0,
|
||||||
type: 'success'
|
type: 'success'
|
||||||
|
},
|
||||||
|
location: {
|
||||||
|
action: 'PUSH',
|
||||||
|
pathname: currentHike && currentHike.dashedName ?
|
||||||
|
`/hikes/${ currentHike.dashedName }` :
|
||||||
|
'/hikes'
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
Reference in New Issue
Block a user