diff --git a/components/flashcards/DeckDetails.js b/components/flashcards/DeckDetails.js
index f83bb6d..a1e9e53 100644
--- a/components/flashcards/DeckDetails.js
+++ b/components/flashcards/DeckDetails.js
@@ -97,7 +97,7 @@ class DeckDetails extends Component {
-
+ this.props.navigation.navigate('Quiz', { deckKey: deck.name })}>
Start Quiz
diff --git a/components/flashcards/Quiz.js b/components/flashcards/Quiz.js
new file mode 100644
index 0000000..1193d32
--- /dev/null
+++ b/components/flashcards/Quiz.js
@@ -0,0 +1,354 @@
+import React, { Component } from 'react';
+import { connect } from 'react-redux';
+import { View, Text, StyleSheet, TouchableOpacity, ActivityIndicator, Alert } from 'react-native';
+
+import { MaterialCommunityIcons } from '@expo/vector-icons';
+
+import { saveScore } from '../../services';
+
+import { COLOR_WHITE, COLOR_B_4, COLOR_A_1, COLOR_B_6, COLOR_SUCCESS, COLOR_BLACK, COLOR_FAILURE } from '../../utils/colors';
+
+const ANSWER = 'A';
+const QUESTION = 'Q';
+const FORWARD = 'F';
+const REWIND = 'RE';
+const RIGHT = true;
+const WRONG = false;
+const WON = 'W';
+const LOST = 'L';
+const TIE = 'T';
+
+class Quiz extends Component {
+
+ static navigationOptions = ({ navigation }) => {
+ const { deckKey } = navigation.state.params;
+
+ return {
+ title: `'${deckKey}' Quiz`,
+ }
+ }
+
+ state = {
+ questionIndex: 0,
+ answerQuestion: ANSWER,
+ questions: []
+ }
+
+ componentDidMount() {
+ if (this.props.cards.length >= 0) {
+ this.setState((prevState) => {
+ return {
+ questions: this.props.cards.map(card => {
+ const c = card;
+ c.correct = null;
+
+ return c;
+ })
+ }
+ })
+ }
+ }
+
+ navigateQuestions(step) {
+
+ const { questionIndex } = this.state;
+ const size = this.state.questions.length;
+
+ this.setState((prevState) => {
+ return {
+ answerQuestion: ANSWER
+ }
+ })
+
+ switch (step) {
+ case FORWARD: {
+
+ if (questionIndex >= size - 1) {
+ return;
+ }
+
+ this.setState((prevState) => {
+ return {
+ questionIndex: questionIndex + 1
+ }
+ });
+ return;
+ }
+ case REWIND: {
+
+ if (questionIndex <= 0) {
+ return;
+ }
+
+ this.setState((prevState) => {
+ return {
+ questionIndex: questionIndex - 1
+ }
+ });
+ return;
+ }
+ }
+
+ }
+
+ showAnswer() {
+ this.setState((prevState) => {
+ return {
+ answerQuestion: QUESTION
+ }
+ })
+ }
+
+ answer(status) {
+ const { questionIndex } = this.state;
+
+ let refreshedQuestions = this.state.questions;
+ refreshedQuestions[questionIndex].correct = status;
+
+ this.setState((prevState) => {
+ return {
+ questions: refreshedQuestions,
+ answerQuestion: ANSWER
+ }
+ });
+ }
+
+ done() {
+
+ Alert.alert(
+ 'Finish quiz?',
+ 'Do you want do finish this quiz?',
+ [
+ { text: 'Yes', onPress: this.finishQuiz.bind(this) },
+ { text: 'No', onPress: () => { } }
+ ]
+ )
+ }
+
+ async finishQuiz() {
+ const { r, w } = this.state.questions.reduce(
+ (currentScore, question) => {
+ if (question.correct) {
+ return {
+ r: currentScore.r + 1,
+ w: currentScore.w
+ }
+ } else {
+ return {
+ r: currentScore.r,
+ w: currentScore.w + 1
+ }
+ }
+ },
+ { r: 0, w: 0 }
+ );
+
+ let title = "";
+ let message = "";
+ let status = null;
+
+ if (r > w) {
+ title = "Congratulations!";
+ message = "You won!";
+ status = WON;
+ } else if (r < w) {
+ title = "Keep studying!";
+ message = "You lost :(";
+ status = LOST;
+ } else {
+ title = "Almost there";
+ message = "It's a tie =|";
+ status = TIE;
+ }
+
+ try {
+ const { navigation } = this.props;
+ const { deckKey } = navigation.state.params;
+
+ await saveScore(deckKey, status);
+ Alert.alert(
+ title,
+ message,
+ [{ text: 'OK', onPress: () => navigation.goBack() }],
+ { cancelable: false }
+ );
+ } catch (e) {
+ console.log(e);
+ }
+
+ }
+
+
+ render() {
+
+ const size = this.state.questions.length;
+
+ if (size === 0) {
+ return (
+
+
+
+ );
+ } else {
+
+ const { questionIndex, answerQuestion } = this.state;
+ const question = this.state.questions[questionIndex];
+
+ return (
+
+
+ { this.navigateQuestions(REWIND) }}>
+
+
+
+ {questionIndex + 1}/{size}
+
+ { this.navigateQuestions(FORWARD) }}>
+
+
+
+ {
+ this.state.answerQuestion === ANSWER
+ ?
+ (
+
+
+ "{question.question}"
+
+
+
+ Answer
+
+
+ Done
+
+
+
+ )
+ :
+ (
+
+
+ "{question.answer}"
+
+
+ this.answer(RIGHT)}>
+ Right =)
+
+ this.answer(WRONG)}>
+ Wrong =(
+
+
+
+ )
+ }
+
+
+ );
+
+ }
+ }
+}
+
+const styles = StyleSheet.create({
+ mainContainer: {
+ flex: 1
+ },
+ topContainer: {
+ flex: 2,
+ flexDirection: 'row',
+ justifyContent: 'space-between'
+ },
+ questionContainer: {
+ flex: 7,
+ alignItems: 'center',
+ justifyContent: 'center',
+ borderWidth: 1,
+ borderRadius: 2,
+ borderColor: COLOR_B_6,
+ margin: 5
+ },
+ btnContainer: {
+ flex: 3
+ },
+ qaWrapper: {
+ flex: 10
+ },
+ btnAnswerContainer: {
+ flex: 3,
+ flexDirection: 'row'
+ },
+ btnArrow: {
+ flex: 2,
+ backgroundColor: COLOR_B_4,
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ numQuestionContainer: {
+ flex: 8,
+ alignItems: 'center',
+ justifyContent: 'center'
+ },
+ numQuestionText: {
+ fontSize: 35,
+ fontWeight: 'bold'
+ },
+ questionText: {
+ fontSize: 25,
+ fontStyle: 'italic'
+ },
+ btn: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: COLOR_B_4,
+ borderRadius: 2,
+ margin: 2
+ },
+ btnDone: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: COLOR_SUCCESS,
+ borderRadius: 2,
+ margin: 2
+ },
+ btnAnswerText: {
+ flex: 1,
+ fontSize: 20,
+ alignSelf: 'center',
+ color: COLOR_A_1,
+ textAlign: 'center'
+ },
+ btnDoneText: {
+ flex: 1,
+ fontSize: 20,
+ alignSelf: 'center',
+ color: COLOR_BLACK,
+ textAlign: 'center'
+ },
+ btnCorrect: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: 'green',
+ borderRadius: 2,
+ margin: 2
+ },
+ btnWrong: {
+ flex: 1,
+ flexDirection: 'row',
+ backgroundColor: COLOR_FAILURE,
+ borderRadius: 2,
+ margin: 2
+ }
+});
+
+function mapStateToProps({ decks }, props) {
+ const { deckKey } = props.navigation.state.params;
+ const deck = decks[deckKey];
+ const cards = deck.cards;
+
+ return {
+ cards
+ };
+}
+
+export default connect(mapStateToProps)(Quiz);
\ No newline at end of file
diff --git a/components/navigators/FlashStackNavigator.js b/components/navigators/FlashStackNavigator.js
index 9c97a97..f2761ed 100644
--- a/components/navigators/FlashStackNavigator.js
+++ b/components/navigators/FlashStackNavigator.js
@@ -9,6 +9,7 @@ import DeckDetails from '../flashcards/DeckDetails';
import { COLOR_A_1, COLOR_B_5 } from '../../utils/colors';
import CardList from '../flashcards/CardList';
+import Quiz from '../flashcards/Quiz';
export default class FlashStackNavigator extends Component {
@@ -25,6 +26,9 @@ export default class FlashStackNavigator extends Component {
},
Cards: {
screen: CardList
+ },
+ Quiz:{
+ screen: Quiz
}
}, {
navigationOptions: {
diff --git a/services/index.js b/services/index.js
index 98d28b6..5336258 100644
--- a/services/index.js
+++ b/services/index.js
@@ -1,14 +1,15 @@
import { AsyncStorage } from 'react-native';
-const FLASHCARDS_STORAGE_KEY = "Flashcards:decks"
+const FLASHCARDS_STORAGE_KEY = "Flashcards:decks";
+const SCORES_STORAGE_KEY = "Flashcards:scores";
-export async function persistDeck(deck){
+export async function persistDeck(deck) {
await AsyncStorage.mergeItem(FLASHCARDS_STORAGE_KEY, JSON.stringify({
[deck.name]: deck
}))
}
-export async function deleteDeck(deckKey){
+export async function deleteDeck(deckKey) {
let decks = await loadDecks();
delete decks[deckKey];
@@ -20,16 +21,16 @@ export async function loadDecks() {
return decks !== null ? decks : [];
}
-export async function getDeck(deckKey){
+export async function getDeck(deckKey) {
const decks = await loadDecks();
const deck = decks[deckKey];
return deck;
}
-export async function addCardToDeck(deckKey, card){
+export async function addCardToDeck(deckKey, card) {
const deck = await getDeck(deckKey);
- if(typeof deck === 'undefined'){
+ if (typeof deck === 'undefined') {
console.warn(`Trying to fetch invalid deck: ${deckKey}`);
return;
}
@@ -38,9 +39,22 @@ export async function addCardToDeck(deckKey, card){
await persistDeck(deck);
}
-export async function deleteCardFromDeck(deckKey, cardName){
+export async function deleteCardFromDeck(deckKey, cardName) {
const deck = await getDeck(deckKey);
deck.cards = deck.cards.filter(card => card.question !== cardName);
await persistDeck(deck);
+}
+
+export async function getScores() {
+ const scores = JSON.parse(await AsyncStorage.getItem(SCORES_STORAGE_KEY));
+ return scores !== null ? scores : [];
+}
+
+export async function saveScore(deck, status) {
+ const scores = await getScores();
+ const date = Date.now();
+ scores.push({ deck, status, date });
+
+ await AsyncStorage.setItem(SCORES_STORAGE_KEY, JSON.stringify(scores));
}
\ No newline at end of file
diff --git a/utils/colors.js b/utils/colors.js
index 6b7d5a3..d6b2c26 100644
--- a/utils/colors.js
+++ b/utils/colors.js
@@ -15,6 +15,7 @@ export const COLOR_B_6 = "#2728ff";
export const COLOR_B_7 = "#0000fe";
export const COLOR_WHITE = "#fff";
+export const COLOR_BLACK = "#000";
export const COLOR_SUCCESS = "#7ee821";
export const COLOR_FAILURE = "#ff2424";