stages ready

This commit is contained in:
Antti Pilto 2016-08-09 00:37:09 +03:00
parent 011db14f38
commit 03a69f276e
13 changed files with 191 additions and 64 deletions

View file

@ -3,6 +3,7 @@ import AuthService from '../../utils/AuthService'
import './App.scss';
import Navbar from '../Navbar/Navbar';
import GameContainer from '../GameContainer/GameContainer';
import { removeHash } from '../../data/helperFuncs';
const options = {};
const auth = new AuthService(__AUTH0_CLIENT_ID__, __AUTH0_DOMAIN__, options);
@ -13,11 +14,9 @@ class App extends Component {
this.state = {
isAuthenticated: this.isAuthenticated(),
gameState: 'game'
/*profile: auth.getProfile()*/
}
auth.on('profile_updated', (newProfile) => {
// do i need a check for valid profile?
this.setState({isAuthenticated: this.isAuthenticated(), /*profile: newProfile*/});
this.setState({isAuthenticated: this.isAuthenticated()});
})
this.logout = this.logout.bind(this);
this.getNickName = this.getNickName.bind(this);
@ -26,11 +25,12 @@ class App extends Component {
}
isAuthenticated() {
return auth.getProfile() && auth.getProfile().hasOwnProperty('user_id');
removeHash();
return auth.loggedIn() && auth.getProfile() && auth.getProfile().hasOwnProperty('user_id');
}
getNickName() {
return auth.getProfile() && auth.getProfile().hasOwnProperty('nickname')?auth.getProfile().nickname:'';
return auth.loggedIn() && auth.getProfile() && auth.getProfile().hasOwnProperty('nickname')?auth.getProfile().nickname:'';
}
startGame() {
@ -48,8 +48,7 @@ class App extends Component {
render() {
let loginButton = !this.state.isAuthenticated ?
<p className="login-button"><a href="#" onClick={auth.login.bind(this)}>Log in to save your progress!</a></p> : '';
<p className="login-button"><a href="javascript:;" onClick={auth.login.bind(this)}>Log in to save your progress!</a></p> : '';
return (
<div>
<Navbar isAuthenticated={this.state.isAuthenticated}
@ -65,11 +64,12 @@ class App extends Component {
nickName={this.getNickName()}
gameState={this.state.gameState}
handleStartGame={this.startGame}
handleEndGame={this.endGame}
/>
</div>
</div>
<div className="row text-center">{loginButton}</div>
</div>
</div>
</div>
)
}
}

View file

@ -12,7 +12,7 @@ body {
background-color: #f5f5f5;
padding-bottom: 20px;
border-bottom: 1px #dadada solid;
min-height: 690px;
min-height: 500px; /* 690 */
}
.container {
margin: 0 auto;

View file

@ -95,7 +95,7 @@ class ChooseCharacters extends Component {
<div className="col-xs-12">
<div className="panel panel-default">
<div className="panel-body welcome">
<h4>Welcome to Kana Quiz{this.props.nickName !== '' && typeof this.props.nickName !== 'undefined'?', '+this.props.nickName:''}!</h4>
<h4>Welcome to Kana Quiz{this.props.isAuthenticated && this.props.nickName !== '' && typeof this.props.nickName !== 'undefined'?', '+this.props.nickName:''}!</h4>
<p>Please choose the groups of characters that you'd like to be studying.</p>
</div>
</div>

View file

@ -8,17 +8,23 @@ class Game extends Component {
constructor(props) {
super(props);
this.state = {
stage:1,
stage:0,
isLocked: false,
showScreen: ''
}
this.showQuestion = this.showQuestion.bind(this);
this.stageUp = this.stageUp.bind(this);
this.lockStage = this.lockStage.bind(this);
}
stageUp() {
this.setState({stage: this.state.stage+1, showScreen: 'stage'});
}
lockStage(stage) {
this.setState({stage: stage, showScreen: 'question', isLocked: true});
}
showQuestion() {
this.setState({showScreen: 'question'})
}
@ -30,8 +36,8 @@ class Game extends Component {
render() {
return (
<div>
{ this.state.showScreen==='stage' ? <ShowStage handleShowQuestion={this.showQuestion} stage={this.state.stage} /> : '' }
{ this.state.showScreen==='question' ? <Question handleStageUp={this.stageUp} stage={this.state.stage} decidedGroups={this.props.decidedGroups} /> : '' }
{ this.state.showScreen==='stage' ? <ShowStage lockStage={this.lockStage} handleShowQuestion={this.showQuestion} handleEndGame={this.props.handleEndGame} stage={this.state.stage} /> : '' }
{ this.state.showScreen==='question' ? <Question isLocked={this.state.isLocked} handleStageUp={this.stageUp} stage={this.state.stage} decidedGroups={this.props.decidedGroups} /> : '' }
</div>
);
}

View file

@ -1,7 +1,7 @@
import React, { Component } from 'react';
import { kanaDictionary } from '../../data/kanaDictionary';
import { quizSettings } from '../../data/quizSettings';
import { findRomajisAtKanaKey, removeFromArray, arrayContains, shuffle } from '../../data/helperFuncs';
import { findRomajisAtKanaKey, removeFromArray, arrayContains, shuffle, cartesianProduct } from '../../data/helperFuncs';
import './Question.scss';
class Question extends Component {
@ -10,12 +10,15 @@ class Question extends Component {
this.state = {
previousQuestion: [],
previousAnswer: '',
currentAnswer: '',
currentQuestion: [],
answerOptions: [],
stageProgress: 0
}
this.setNewQuestion = this.setNewQuestion.bind(this);
this.handleAnswer = this.handleAnswer.bind(this);
this.handleAnswerChange = this.handleAnswerChange.bind(this);
this.handleSubmit = this.handleSubmit.bind(this);
}
getRandomKanas(amount, include, exclude) {
@ -40,7 +43,10 @@ class Question extends Component {
}
setNewQuestion() {
if(this.props.stage!=4)
this.currentQuestion = this.getRandomKanas(1, false, this.previousQuestion);
else
this.currentQuestion = this.getRandomKanas(3, false, this.previousQuestion);
this.setState({currentQuestion: this.currentQuestion});
this.setAnswerOptions();
this.setAllowedAnswers();
@ -54,9 +60,24 @@ class Question extends Component {
}
setAllowedAnswers() {
if(this.props.stage==1) this.allowedAnswers = findRomajisAtKanaKey(this.currentQuestion);
if(this.props.stage==2) this.allowedAnswers = this.currentQuestion;
// console.log("allowed answers: "+this.allowedAnswers);
// console.log(this.currentQuestion);
this.allowedAnswers = [];
if(this.props.stage==1 || this.props.stage==3)
this.allowedAnswers = findRomajisAtKanaKey(this.currentQuestion, kanaDictionary);
else if(this.props.stage==2)
this.allowedAnswers = this.currentQuestion;
else if(this.props.stage==4) {
let tempAllowedAnswers = [];
this.currentQuestion.map(function(key, idx) {
tempAllowedAnswers.push(findRomajisAtKanaKey(key, kanaDictionary));
}, this);
cartesianProduct(tempAllowedAnswers).map(function(answer) {
this.allowedAnswers.push(answer.join(''));
}, this);
}
console.log(this.allowedAnswers);
}
handleAnswer(answer) {
@ -67,9 +88,16 @@ class Question extends Component {
this.setState({previousAnswer: this.previousAnswer});
this.previousAllowedAnswers = this.allowedAnswers;
if(this.isInAllowedAnswers(this.previousAnswer))
this.setState({stageProgress: this.state.stageProgress+1});
this.stageProgress = this.stageProgress+1;
else
this.stageProgress = this.stageProgress > 0 ? this.stageProgress - 1 : 0;
this.setState({stageProgress: this.stageProgress});
if(this.stageProgress >= quizSettings.stageLength[this.props.stage] &&
!this.props.isLocked) {
let that = this;
setTimeout(function() { that.props.handleStageUp(); }, 300);
}
else
this.setState({stageProgress: this.state.stageProgress > 0 ? this.state.stageProgress-1 : 0});
this.setNewQuestion();
}
@ -79,6 +107,7 @@ class Question extends Component {
this.askableRomajis = [];
this.previousQuestion = '';
this.previousAnswer = '';
this.stageProgress = 0;
Object.keys(kanaDictionary).map(function(whichKana) {
// console.log(whichKana); // 'hiragana' or 'katakana'
Object.keys(kanaDictionary[whichKana]).map(function(groupName) {
@ -105,7 +134,7 @@ class Question extends Component {
getShowableQuestion() {
if(this.getAnswerType()=='kana')
return findRomajisAtKanaKey(this.state.currentQuestion)[0];
return findRomajisAtKanaKey(this.state.currentQuestion, kanaDictionary)[0];
else return this.state.currentQuestion;
}
@ -115,7 +144,7 @@ class Question extends Component {
if(this.previousQuestion=='')
resultString = <div className="previous-result none">Let's go! Which character is this?</div>
else {
let rightAnswer = (this.props.stage==2?findRomajisAtKanaKey(this.previousQuestion)[0]:this.previousQuestion)+' = '+
let rightAnswer = (this.props.stage==2?findRomajisAtKanaKey(this.previousQuestion, kanaDictionary)[0]:this.previousQuestion.join(''))+' = '+
this.previousAllowedAnswers[0];
if(this.isInAllowedAnswers(this.previousAnswer))
resultString = <div className="previous-result correct" title="Correct answer!"><span className="pull-left glyphicon glyphicon-none"></span>{rightAnswer}<span className="pull-right glyphicon glyphicon-ok"></span></div>
@ -125,9 +154,6 @@ class Question extends Component {
return resultString;
}
getStageProgress() {
}
isInAllowedAnswers(previousAnswer) {
// console.log(previousAnswer);
// console.log(this.allowedAnswers);
@ -136,6 +162,18 @@ class Question extends Component {
else return false;
}
handleAnswerChange(e) {
this.setState({currentAnswer: e.target.value.replace(/\s+/g, '')});
}
handleSubmit(e) {
e.preventDefault();
if(this.state.currentAnswer!='') {
this.handleAnswer(this.state.currentAnswer.toLowerCase());
this.setState({currentAnswer: ''});
}
}
componentWillMount() {
this.initializeCharacters();
}
@ -155,13 +193,19 @@ class Question extends Component {
{this.getPreviousResult()}
<div className="big-character">{this.getShowableQuestion()}</div>
<div className="answer-container">
{this.state.answerOptions.map(function(answer, idx) {
{this.props.stage<3?this.state.answerOptions.map(function(answer, idx) {
return <AnswerButton answer={answer}
className={btnClass}
key={idx}
answertype={this.getAnswerType()}
handleAnswer={this.handleAnswer} />
}, this)}
}, this):
<div className="answer-form-container">
<form onSubmit={this.handleSubmit}>
<input autoFocus className="answer-input" type="text" value={this.state.currentAnswer} onChange={this.handleAnswerChange} />
</form>
</div>
}
</div>
<div className="progress">
<div className="progress-bar progress-bar-info"
@ -183,7 +227,7 @@ class Question extends Component {
class AnswerButton extends Component {
getShowableAnswer() {
if(this.props.answertype=='romaji')
return findRomajisAtKanaKey(this.props.answer)[0];
return findRomajisAtKanaKey(this.props.answer, kanaDictionary)[0];
else return this.props.answer;
}

View file

@ -21,6 +21,9 @@
margin: 30px auto 28px;
border-radius: 3px;
}
.size-up {
font-size: 1.1em;
}
.none {
background-color: #aaa;
color: #f5f5f5;
@ -48,11 +51,25 @@
display: flex;
justify-content: space-between;
}
.answer-form-container {
max-width: 100px;
margin: 0 auto;
}
.answer-button {
min-width: 90px;
font-size: 2em;
margin: 30px 0 60px;
}
.answer-input, .answer-input:focus {
outline: none;
width: 110px;
text-align: center;
font-size: 2em;
margin: 25px 0 60px;
background: none;
border: none;
border-bottom: solid 1px #aaa;
}
.no-hover {
/* disables hover effect on touch screens */
background-color: #fff;

View file

@ -15,7 +15,7 @@ class ShowStage extends Component {
removeStage() {
this.setState({stageContent: ''});
clearTimeout(this.timeoutID);
this.timeoutID = setTimeout(this.props.handleShowQuestion, 1) // how soon we go to question (1000)
this.timeoutID = setTimeout(this.props.handleShowQuestion, 1000) // how soon we go to question (1000)
}
showStage() {
@ -25,12 +25,27 @@ class ShowStage extends Component {
if(this.props.stage===2) { stageDescription = 'Choose one'; stageSecondaryDescription = 'Reverse'; }
if(this.props.stage===3) stageDescription = 'Write the answer';
if(this.props.stage===4) { stageDescription = 'Write the answer'; stageSecondaryDescription = 'Three at once'; }
let stageContent = <div><h1>Stage {this.props.stage}</h1><h3>{stageDescription}</h3>{stageSecondaryDescription?<h4>{stageSecondaryDescription}</h4>:''}</div>;
let stageContent = (<div className="text-center show-stage">
<h1>Stage {this.props.stage}</h1>
<h3>{stageDescription}</h3>
{stageSecondaryDescription?<h4>{stageSecondaryDescription}</h4>:''}
</div>);
if(this.props.stage===5) {
stageContent = (<div className="text-center show-end">
<h1>Congratulations!</h1>
<h3>You have passed all 4 stages.</h3>
<h4>Would you like to keep playing or go back to menu?</h4>
<p><button className="btn btn-danger keep-playing" onClick={()=>this.props.lockStage(4)}>Keep playing</button></p>
<p><button className="btn btn-danger back-to-menu" onClick={this.props.handleEndGame}>Back to menu</button></p>
</div>)
}
this.setState({stageContent: stageContent});
}
componentDidMount() {
this.timeoutID = setTimeout(this.removeStage, 1); // how soon we start fading out (1500)
if(this.props.stage<=4)
this.timeoutID = setTimeout(this.removeStage, 1500); // how soon we start fading out (1500)
this.showStage();
}
@ -40,11 +55,9 @@ class ShowStage extends Component {
render() {
return (
<div className="text-center show-stage">
<ReactCSSTransitionGroup transitionName="stage" transitionEnterTimeout={1000} transitionLeaveTimeout={1000}>
{this.state.stageContent}
</ReactCSSTransitionGroup>
</div>
);
}
}

View file

@ -1,9 +1,21 @@
.keep-playing {
margin: 20px 0 20px;
}
.back-to-menu {
margin: 0 0 20px;
}
.show-stage {
margin-top: 200px;
margin-top: 130px;
h3 {
margin-top: 30px;
}
}
.show-end {
margin-top: 80px;
h3, h4 {
margin-top: 20px;
}
}
.stage-enter {
opacity: 0.01;

View file

@ -24,10 +24,12 @@ class GameContainer extends Component {
{ this.props.gameState==='chooseCharacters' ?
<ChooseCharacters selectedGroups={this.state.decidedGroups}
handleStartGame={this.startGame}
isAuthenticated={this.props.isAuthenticated}
nickName={this.props.nickName}
/> : '' }
{ this.props.gameState==='game' ?
<Game decidedGroups={this.state.decidedGroups}
handleEndGame={this.props.handleEndGame}
/> : '' }
</div>
)

View file

@ -10,11 +10,11 @@ class Navbar extends Component {
leftLink = <li id="nav-kanaquiz"><p className="nav navbar-text">Kana Quiz <span>2</span></p></li>
break;
case 'game':
leftLink = <li id="nav-choosecharacters"><a href="#" onClick={this.props.handleEndGame}><span className="glyphicon glyphicon-small glyphicon-arrow-left"></span> Back to menu</a></li>
leftLink = <li id="nav-choosecharacters"><a href="javascript:;" onClick={this.props.handleEndGame}><span className="glyphicon glyphicon-small glyphicon-arrow-left"></span> Back to menu</a></li>
}
let profileButton = this.props.isAuthenticated ?
<a href="#" onClick={this.props.handleLogout}><span className="glyphicon glyphicon-small glyphicon-log-out"></span> Log out</a> :
<a href="#" onClick={this.props.handleLogin}><span className="glyphicon glyphicon-small glyphicon-log-in"></span> Log in</a>;
<a href="javascript:;" onClick={this.props.handleLogout}><span className="glyphicon glyphicon-small glyphicon-log-out"></span> Log out</a> :
<a href="javascript:;" onClick={this.props.handleLogin}><span className="glyphicon glyphicon-small glyphicon-log-in"></span> Log in</a>;
return (
<nav className="navbar navbar-inverse navbar-fixed-top" role="navigation">
<div className="container">

View file

@ -1,5 +1,3 @@
import { kanaDictionary } from './kanaDictionary';
export function arrayContains(needle, haystack) {
return (haystack.indexOf(needle) > -1) ? true : false;
}
@ -14,13 +12,12 @@ export function removeFromArray(needle, haystack) {
return haystack;
}
export function findRomajisAtKanaKey(needle) {
export function findRomajisAtKanaKey(needle, kanaDictionary) {
let romaji = [];
Object.keys(kanaDictionary).map(function(whichKana) {
// console.log(whichKana); // 'hiragana' or 'katakana'
Object.keys(kanaDictionary[whichKana]).map(function(groupName) {
// console.log(groupName); // 'h_group1', ...
// return kanaDictionary[whichKana][groupName]['characters'][this.props.answer];
Object.keys(kanaDictionary[whichKana][groupName]['characters']).map(function(key) {
if(key==needle) {
// console.log(kanaDictionary[whichKana][groupName]['characters'][key]);
@ -32,13 +29,6 @@ export function findRomajisAtKanaKey(needle) {
// console.log(romaji);
return romaji;
}
// export function getRandomFromArray(arr) {
// return arr[Math.floor(Math.random() * arr.length)];
// }
// export function getIndex(needle, haystack) {
// return haystack.indexOf(needle);
// }
export function shuffle(array) {
var i = 0
@ -52,3 +42,46 @@ export function shuffle(array) {
array[j] = temp
}
}
export function removeHash () {
var loc = window.location;
if ("pushState" in history)
history.replaceState("", document.title, loc.pathname + loc.search);
}
export function getRandomFromArray(arr) {
return arr[Math.floor(Math.random() * arr.length)];
}
export function cartesianProduct(elements) {
if (!Array.isArray(elements)) {
throw new TypeError();
}
var end = elements.length - 1,
result = [];
function addTo(curr, start) {
var first = elements[start],
last = (start === end);
for (var i = 0; i < first.length; ++i) {
var copy = curr.slice();
copy.push(first[i]);
if (last) {
result.push(copy);
} else {
addTo(copy, start + 1);
}
}
}
if (elements.length) {
addTo([], 0);
} else {
result.push([]);
}
return result;
}

View file

@ -1,8 +1,8 @@
export const quizSettings = {
stageLength : {
1: 20,
2: 20,
3: 20,
4: 20
1: 10,
2: 10,
3: 10,
4: 10
}
};

View file

@ -13,7 +13,7 @@ export function getTokenExpirationDate(token){
export function isTokenExpired(token){
const date = getTokenExpirationDate(token)
const offsetSeconds = 0
const offsetSeconds = 60*60*24*7
if (date === null) {
return false
}