import { h, render, Component } from 'preact'; // eslint-disable-line no-unused-vars
// content and config:
import content from '../content/module.json';
import config from '../config';
// services and helper:
import api from '../utilities/api';
import { fixedDigits } from '../utilities/formatter';
// screens and items:
import TitleScreen from './TitleScreen.jsx';
import FinalScreen from './FinalScreen.jsx';
import QuestionScreen from './QuestionScreen.jsx';
/**
* titlescreen, questionscreen (repeat), finalscreen
*/
export default class App extends Component {
// construct and initialize functions
constructor (props) {
super(props);
// for example: (1400 - 200) / 3 -> 400 points per section
const diff = (config.numberOfPoints.first - config.numberOfPoints.last) / 3;
// for example: 1400, 1000, 600, 200
this.numberOfPoints = [
parseInt(config.numberOfPoints.first, 10),
parseInt(config.numberOfPoints.first - diff, 10),
parseInt(config.numberOfPoints.last + diff, 10),
parseInt(config.numberOfPoints.last, 10)
];
this.state = {
route: 'titlescreen', // current screen
isFetching: false, // currently performing XHR?
userId: null, // current user id for posting
token: null, // current access token
mode: 'intro', // current mode: init || intro || question || score
currentQuestion: 0, // current question number
currentAnswerIndex: null, // index in question pair of the currently set answer
currentAnswerCorrect: false, // is the currently set question the correct one?
currentNumberOfPoints: this.numberOfPoints[0], // current number of points
nextNumberOfPoints: this.numberOfPoints[1], // next number of points
currentAmount: config.highlightedAmount, // curent amount of points to be highlighted
voteRatios: null // get votes from other users
};
if (window.location.hash) this.state.route = window.location.hash.replace('#', '');
// context binding
this.navigate = this.navigate.bind(this);
this.toNextQuestion = this.toNextQuestion.bind(this);
this.setAnswer = this.setAnswer.bind(this);
this.getUserVotes = this.getUserVotes.bind(this);
this.reset = this.reset.bind(this);
this.setup = this.setup.bind(this);
this.endUserSession = this.endUserSession.bind(this);
}
// to next question
toNextQuestion () {
// intro screen, introduce bubbles :P
if (this.state.mode === 'intro') {
this.setState({
// currentQuestion: null,
mode: 'init',
route: 'question'
});
} else if (this.state.currentQuestion === 0 && this.state.mode === 'init') {
// is first question?
this.setState({
currentAmount: 0,
mode: 'question',
route: 'question'
});
} else if (this.state.currentQuestion === 3 && this.state.mode === 'score') {
// is last question? go to final screen
this.setState({
route: 'finalscreen',
mode: 'intro',
currentNumberOfPoints: this.numberOfPoints[0],
currentAmount: config.highlightedAmount,
currentQuestion: 0,
currentAnswerIndex: null,
currentAnswerCorrect: false,
nextNumberOfPoints: this.numberOfPoints[1],
voteRatios: null
});
} else if (this.state.voteRatios && this.state.mode === 'question') {
// go to score screen
this.setState({
route: 'question',
mode: 'score'
});
} else {
// go to next question
this.setState({
route: 'question',
mode: 'question',
currentQuestion: this.state.currentQuestion + 1,
currentAnswerIndex: null,
currentNumberOfPoints: this.numberOfPoints[this.state.currentQuestion + 1],
nextNumberOfPoints: this.numberOfPoints[this.state.currentQuestion + 2],
currentAmount: 0,
voteRatios: null
});
}
}
// set and check answer
setAnswer (index, isCorrect) {
if (this.state.currentAnswerIndex === null) {
// check answer
this.setState({
currentAnswerIndex: index,
currentAnswerCorrect: isCorrect,
mode: 'question'
});
// submit answer
if (!this.props.isOffline) {
// only send answer for current question
const payload = { userId: this.state.userId };
payload[`answerQ${this.state.currentQuestion + 1}`] = this.state.currentAnswerIndex + 1;
api.post(config.api.create, payload, this.state.token).then(() => this.getUserVotes());
} else {
this.getUserVotes();
}
}
}
// get user votes from api
getUserVotes () {
if (!this.props.isOffline) {
api.get(config.api.proportions, { question: this.state.currentQuestion + 1 }).then(
json => this.setState({ voteRatios: [ json.anteile.richtig, json.anteile.falsch ] })
);
} else {
this.setState({ voteRatios: [] });
}
}
// initial setup
setup () {
if (!this.props.isOffline) {
this.setState({ isFetching: true });
api.getToken().then(accessToken => {
this.setState({ token: accessToken });
// create user
api.createUser(accessToken)
.then(user => {
this.setState({ userId: user.userId, isFetching: false });
this.jumpTo();
});
});
}
}
// shortcut: jump to specific screen using `#{something}`
jumpTo () {
if (window.location.hash) {
const hashInfo = window.location.hash.replace('#', '').split('_');
this.setState({
route: 'question',
mode: hashInfo[0],
currentAmount: 0,
currentAnswerCorrect: null,
currentAnswerIndex: null,
currentNumberOfPoints: 1000,
currentQuestion: parseInt(hashInfo[1], 10),
nextNumberOfPoints: 200,
voteRatios: null
});
}
}
// set navigation state
navigate (route) {
this.setState({ route });
}
// finish user session
endUserSession () {
api.endSession(this.state.userId, this.state.token);
}
// reset everything to restart the module
reset () {
this.setState({ route: 'titlescreen' });
this.setup();
}
// LIFECYLCE
componentWillMount () {
this.setup();
}
// RENDER
render () {
let outputContent;
// get header content
const total = fixedDigits(config.numberQuestions, 2);
const index = this.state.currentQuestion + 1; // 0-indexed
const headerState = `${fixedDigits(index, 2)}/${total}`;
switch (this.state.route) {
case 'question':
outputContent = ;
break;
case 'finalscreen':
outputContent = ;
break;
case 'titlescreen':
default:
outputContent = ;
break;
}
return outputContent;
}
}