diff --git a/src/ClassicTestStart.js b/src/ClassicTestStart.js
new file mode 100644
index 0000000..fcf52b3
--- /dev/null
+++ b/src/ClassicTestStart.js
@@ -0,0 +1,78 @@
+import React from "react";
+import { CloseRounded as CloseRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon } from "@material-ui/icons";
+import Button from "./Button";
+import Checkbox from '@material-ui/core/Checkbox';
+
+import Slider from "rc-slider";
+import "rc-slider/assets/index.css";
+
+export default function ClassicTestStart(props) {
+ return (
+ <>
+
+
+
Number of Questions
+
+
+ div")
+ ).getPropertyValue("--text-color")
+ .trim(),
+ border: 0,
+ }}
+ handleStyle={{
+ backgroundColor: getComputedStyle(
+ document.querySelector("#root > div")
+ ).getPropertyValue("--primary-color")
+ .trim(),
+ border: 0,
+ }}
+ trackStyle={{
+ backgroundColor: getComputedStyle(
+ document.querySelector("#root > div")
+ ).getPropertyValue("--primary-color-tinted")
+ .trim(),
+ }}
+ />
+ !isNaN(Number(event.key))}
+ onChange={(event) => props.onSliderChange(Number(event.target.value))}
+ value={props.sliderValue}
+ min={1}
+ max={props.max}
+ size="1"
+ />
+
+
+
+
+
+
+
}
+ className="button--no-background popup-close-button"
+ >
+
+ >
+ )
+}
diff --git a/src/CountdownTestStart.js b/src/CountdownTestStart.js
new file mode 100644
index 0000000..e057fd4
--- /dev/null
+++ b/src/CountdownTestStart.js
@@ -0,0 +1,16 @@
+import React from "react";
+
+export default function CountdownTestStart(props) {
+ return (
+ <>
+
+
+ Awaiting content
+
+
+ Cancel
+
+
+ >
+ )
+}
diff --git a/src/LivesTestStart.js b/src/LivesTestStart.js
new file mode 100644
index 0000000..d5e5cec
--- /dev/null
+++ b/src/LivesTestStart.js
@@ -0,0 +1,78 @@
+import React from "react";
+import { CloseRounded as CloseRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon } from "@material-ui/icons";
+import Button from "./Button";
+import Checkbox from '@material-ui/core/Checkbox';
+
+import Slider from "rc-slider";
+import "rc-slider/assets/index.css";
+
+export default function LivesTestStart(props) {
+ return (
+ <>
+
+
+
Number of Lives
+
+
+ div")
+ ).getPropertyValue("--text-color")
+ .trim(),
+ border: 0,
+ }}
+ handleStyle={{
+ backgroundColor: getComputedStyle(
+ document.querySelector("#root > div")
+ ).getPropertyValue("--primary-color")
+ .trim(),
+ border: 0,
+ }}
+ trackStyle={{
+ backgroundColor: getComputedStyle(
+ document.querySelector("#root > div")
+ ).getPropertyValue("--primary-color-tinted")
+ .trim(),
+ }}
+ />
+ !isNaN(Number(event.key))}
+ onChange={(event) => props.onSliderChange(Number(event.target.value))}
+ value={props.sliderValue}
+ min={1}
+ max={props.max}
+ size="1"
+ />
+
+
+
+
+
+
+
}
+ className="button--no-background popup-close-button"
+ >
+
+ >
+ )
+}
diff --git a/src/LoggedInHome.js b/src/LoggedInHome.js
index 942fe7c..ae73539 100644
--- a/src/LoggedInHome.js
+++ b/src/LoggedInHome.js
@@ -12,9 +12,16 @@ import "firebase/functions";
import Button from './Button';
import LinkButton from './LinkButton';
import Footer from "./Footer";
+import TestStart from './TestStart';
+import ClassicTestStart from './ClassicTestStart';
+import LivesTestStart from './LivesTestStart';
+import CountdownTestStart from './CountdownTestStart';
import "./css/Form.css";
import "./css/History.css";
import "./css/LoggedInHome.css";
+import "./css/PopUp.css";
+import "./css/OptionsListOverlay.css";
+import "./css/SliderOverlay.css";
import { withRouter } from "react-router-dom";
@@ -54,6 +61,12 @@ export default withRouter(class LoggedInHome extends React.Component {
}
],
progressHistoryIncomplete: [],
+ showTestStart: false,
+ showClassicTestStart: false,
+ showLivesTestStart: false,
+ sliderValue: 1,
+ switchLanguage: false,
+ totalTestQuestions: 1,
};
let isMounted = true;
@@ -177,15 +190,15 @@ export default withRouter(class LoggedInHome extends React.Component {
});
}
- startTest = () => {
+ startTest = (mode) => {
if (this.state.canStartTest) {
const selections = Object.keys(this.state.selections)
.filter(x => this.state.selections[x]);
this.state.functions.createProgress({
sets: selections,
- switch_language: false,
- mode: "questions",
- limit: 1000,
+ switch_language: this.state.switchLanguage,
+ mode: mode,
+ limit: this.state.sliderValue,
}).then((result) => {
const progressId = result.data;
this.stopLoading();
@@ -231,6 +244,94 @@ export default withRouter(class LoggedInHome extends React.Component {
});
}
+ showTestStart = () => {
+ if (this.state.canStartTest) {
+ this.setState({
+ showTestStart: true,
+ totalTestQuestions: 1,
+ });
+ }
+ }
+
+ hideTestStart = () => {
+ this.setState({
+ showTestStart: false,
+ });
+ }
+
+ showIndividualTestPrompt = async (mode) => {
+ if (!this.state.loading) {
+ if (mode === "classic") {
+ this.setState({
+ loading: true,
+ })
+ const setIds = Object.keys(this.state.selections)
+ .filter(x => this.state.selections[x]);
+
+ const totalTestQuestions = (await Promise.all(setIds.map((setId) =>
+ this.state.db.collection("sets")
+ .doc(setId)
+ .collection("vocab")
+ .get()
+ .then(querySnapshot => querySnapshot.docs.length)
+ ))).reduce((a, b) => a + b);
+
+ this.setState({
+ showTestStart: false,
+ showClassicTestStart: true,
+ sliderValue: totalTestQuestions,
+ switchLanguage: false,
+ totalTestQuestions: totalTestQuestions,
+ loading: false,
+ });
+ } else if (mode === "lives") {
+ this.setState({
+ showTestStart: false,
+ showLivesTestStart: true,
+ switchLanguage: false,
+ sliderValue: 5,
+ });
+ } else {
+ // countdown
+ // this.setState({
+ // showTestStart: false,
+ // showCountdownTestStart: true,
+ // switchLanguage: false,
+ // });
+ }
+ }
+ }
+
+ hideClassicTestStart = () => {
+ this.setState({
+ showClassicTestStart: false,
+ });
+ }
+
+ hideLivesTestStart = () => {
+ this.setState({
+ showLivesTestStart: false,
+ });
+ }
+
+ hideCountdownTestStart = () => {
+ this.setState({
+ showCountdownTestStart: false,
+ });
+ }
+
+ changeSliderValue = (value) => {
+ if (value >= 1 && value <= 999) this.setState({
+ sliderValue: value,
+ });
+ }
+
+ handleSwitchLanguageChange = (event) => {
+ this.setState({
+ switchLanguage: event.target.checked,
+ });
+ }
+
render() {
return (
@@ -241,8 +342,7 @@ export default withRouter(class LoggedInHome extends React.Component {
Study
} className="button--round buttons--mobile">
+
+
+
+ Groups
+
+ {
+ this.state.userSets && this.state.userSets.length > 0 &&
+
+ My Sets
+
+ }
+
{
this.state.progressHistoryIncomplete.length > 0 &&
<>
Incomplete Tests
-
+
Set
Progress
Mark
Grade
Mode
+
{
this.state.progressHistoryIncomplete.map((progressItem) =>
@@ -313,28 +435,6 @@ export default withRouter(class LoggedInHome extends React.Component {
>
}
-
-
-
- Groups
-
- {
- this.state.userSets && this.state.userSets.length > 0 &&
-
- My Sets
-
- }
-
{
this.state.userSets && this.state.userSets.length > 0
@@ -440,6 +540,48 @@ export default withRouter(class LoggedInHome extends React.Component {
+
+ {
+ this.state.showTestStart &&
+
+ }
+ {
+ this.state.showClassicTestStart &&
+
+ }
+ {
+ this.state.showLivesTestStart &&
+
+ }
+ {
+ this.state.showCountdownTestStart &&
+
+ }
)
}
diff --git a/src/Progress.js b/src/Progress.js
index 48550c3..03a76d8 100644
--- a/src/Progress.js
+++ b/src/Progress.js
@@ -1,6 +1,6 @@
import React from 'react';
import { withRouter } from "react-router-dom";
-import { HomeRounded as HomeRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon, SettingsRounded as SettingsRoundedIcon, CloseRounded as CloseRoundedIcon } from "@material-ui/icons";
+import { HomeRounded as HomeRoundedIcon, ArrowForwardRounded as ArrowForwardRoundedIcon, SettingsRounded as SettingsRoundedIcon, CloseRounded as CloseRoundedIcon, PeopleRounded as PeopleRoundedIcon, QuestionAnswerRounded as QuestionAnswerRoundedIcon } from "@material-ui/icons";
import NavBar from "./NavBar";
import Button from "./Button";
import LinkButton from "./LinkButton";
@@ -66,6 +66,9 @@ export default withRouter(class Progress extends React.Component {
attemptHistory: {},
questions: [],
originalTotalQuestions: 1,
+ lives: 1,
+ startLives: null,
+ setComplete: false,
};
let isMounted = true;
@@ -102,10 +105,12 @@ export default withRouter(class Progress extends React.Component {
nextPrompt: null,
setIds: data.setIds,
originalTotalQuestions: [...new Set(data.questions)].length,
+ setComplete: data.duration !== null,
};
if (data.lives) {
newState.lives = data.lives;
+ newState.startLives = data.start_lives;
}
return [ newState, data.duration !== null, data.incorrect, data.duration ];
@@ -152,7 +157,7 @@ export default withRouter(class Progress extends React.Component {
.get()
.then((querySnapshot) => {
newState.attemptNumber = querySnapshot.docs.map((doc) => doc.id).indexOf(this.props.match.params.progressId) + 1;
- if (newState.attemptNumber > 1)
+ if (querySnapshot.docs.length > 1)
newState.attemptHistory = querySnapshot.docs.filter((doc) => doc.data().duration !== null)
.map((doc) => ({
x: new Date(doc.data().start_time),
@@ -308,8 +313,10 @@ export default withRouter(class Progress extends React.Component {
progress_id: this.props.match.params.progressId,
});
}
+
+ if (this.state.mode === "lives") newState.lives = data.lives;
- if (data.correct && !data.moreAnswers && this.state.incorrectAnswers[data.currentVocabId]) {
+ if ((data.correct || data.lives === 0) && !data.moreAnswers && this.state.incorrectAnswers[data.currentVocabId]) {
// all answers to question given correctly
// answer was previously wrong
// store correct answer
@@ -318,10 +325,11 @@ export default withRouter(class Progress extends React.Component {
} else if (!data.correct) {
// incorrect answer given
// store prompt and count=0
+ // store answer if in lives mode and no lives left
newState.incorrectAnswers = this.state.incorrectAnswers;
newState.incorrectAnswers[data.currentVocabId] = {
prompt: this.state.currentPrompt,
- answer: "",
+ answer: data.lives === 0 ? data.correctAnswers : "",
count: 0,
};
}
@@ -343,7 +351,7 @@ export default withRouter(class Progress extends React.Component {
.get()
.then((querySnapshot) => {
newState.attemptNumber = querySnapshot.docs.map((doc) => doc.id).indexOf(this.props.match.params.progressId) + 1;
- if (newState.attemptNumber > 1)
+ if (querySnapshot.docs.length > 1)
newState.attemptHistory = querySnapshot.docs.filter((doc) => doc.data().duration !== null)
.map((doc) => ({
x: new Date(doc.data().start_time),
@@ -360,7 +368,7 @@ export default withRouter(class Progress extends React.Component {
}
data.incorrectAnswers.map((vocabId) => {
- if (newState.incorrectAnswers[vocabId]) {
+ if (newState.incorrectAnswers[vocabId] && newState.incorrectAnswers[vocabId].answer !== "") {
// already been logged including prompt and correct answer
newState.incorrectAnswers[vocabId].count++;
} else {
@@ -429,15 +437,16 @@ export default withRouter(class Progress extends React.Component {
loading: false,
canProceed: true,
};
-
+
if (!this.state.moreAnswers) {
+ if (this.state.nextPrompt === null) newState.setComplete = true;
newState.currentCorrect = [];
newState.currentPrompt = this.state.nextPrompt;
newState.currentSound = this.state.nextSound;
newState.currentSetOwner = this.state.nextSetOwner;
}
- this.setState(newState, () => (this.isMounted) && this.answerInput.focus());
+ this.setState(newState, () => (this.isMounted && !this.state.setComplete) && this.answerInput.focus());
}
}
@@ -464,7 +473,7 @@ export default withRouter(class Progress extends React.Component {
<>
{
- this.state.currentAnswerStatus === null
+ this.state.currentAnswerStatus === null && !this.state.setComplete
?
@@ -512,11 +521,24 @@ export default withRouter(class Progress extends React.Component {
:
- this.state.nextPrompt === null && !this.state.moreAnswers
+ this.state.nextPrompt === null && !this.state.moreAnswers && this.state.setComplete
?
{/* DONE */}
- {this.state.setTitle}
+
+
+ {this.state.setTitle}
+
+ {
+ this.state.mode === "questions"
+ ?
+
+ :
+
+ }
+
+
+
You got
@@ -534,6 +556,14 @@ export default withRouter(class Progress extends React.Component {
Attempt #
{this.state.attemptNumber}
+ {
+ this.state.startLives &&
+
+
with
+
{this.state.startLives}
+
lives
+
+ }
{
this.state.incorrectAnswers && Object.keys(this.state.incorrectAnswers).length > 0 &&
@@ -560,7 +590,7 @@ export default withRouter(class Progress extends React.Component {
>
}
- {this.state.attemptNumber > 1 &&
+ {Object.keys(this.state.attemptHistory).length > 1 &&
<>
History
@@ -625,14 +655,15 @@ export default withRouter(class Progress extends React.Component {
}
{
- (this.state.currentAnswerStatus === null ||
- !(this.state.nextPrompt === null && !this.state.moreAnswers)) &&
+ !this.state.setComplete &&
0 ? this.state.correct / this.state.totalQuestions * 100 : 0}
+ grade={(this.state.correct + this.state.incorrect) > 0 ? this.state.correct / (this.state.correct + this.state.incorrect) * 100 : 0}
+ maxQuestions={this.state.mode === "lives" ? this.state.originalTotalQuestions : null}
/>
}
diff --git a/src/ProgressStats.js b/src/ProgressStats.js
index 3d18c43..ed24f36 100644
--- a/src/ProgressStats.js
+++ b/src/ProgressStats.js
@@ -10,7 +10,17 @@ export default function ProgressStats(props) {
{props.correct}
-
correct
+ {
+ props.maxQuestions
+ ?
+ <>
+
correct of
+
{props.maxQuestions}
+
possible questions
+ >
+ :
+
correct
+ }
{props.incorrect}
@@ -22,8 +32,8 @@ export default function ProgressStats(props) {
-
0 && props.totalVocabItems > 0 ? `${(props.correct / props.totalVocabItems * 100)}%` : "0" }}>
-
{props.correct}/{props.totalVocabItems}
+
0 && props.progressDenominator > 0 ? `${(props.progressNumerator / props.progressDenominator * 100)}%` : "0" }}>
+
{props.progressNumerator}/{props.progressDenominator}
diff --git a/src/SetPage.js b/src/SetPage.js
index 9a47860..93d72d6 100644
--- a/src/SetPage.js
+++ b/src/SetPage.js
@@ -6,6 +6,10 @@ import Button from "./Button";
import LinkButton from "./LinkButton";
import Error404 from "./Error404";
import Footer from "./Footer";
+import TestStart from './TestStart';
+import ClassicTestStart from './ClassicTestStart';
+import LivesTestStart from './LivesTestStart';
+import CountdownTestStart from './CountdownTestStart';
import "./css/PopUp.css";
import "./css/SetPage.css";
@@ -43,6 +47,12 @@ export default withRouter(class SetPage extends React.Component {
groups: {},
currentSetGroups: [],
showDeleteConfirmation: false,
+ showTestStart: false,
+ showClassicTestStart: false,
+ showLivesTestStart: false,
+ sliderValue: 1,
+ switchLanguage: false,
+ totalTestQuestions: 1,
};
let isMounted = true;
@@ -113,13 +123,13 @@ export default withRouter(class SetPage extends React.Component {
});
}
- startTest = () => {
+ startTest = (mode) => {
if (this.state.canStartTest) {
this.state.functions.createProgress({
sets: [this.props.match.params.setId],
- switch_language: false,
- mode: "questions",
- limit: 1000,
+ switch_language: this.state.switchLanguage,
+ mode: mode,
+ limit: this.state.sliderValue,
}).then((result) => {
const progressId = result.data;
this.stopLoading();
@@ -233,6 +243,94 @@ export default withRouter(class SetPage extends React.Component {
});
}
+ showTestStart = () => {
+ if (this.state.canStartTest) {
+ this.setState({
+ showTestStart: true,
+ totalTestQuestions: 1,
+ });
+ }
+ }
+
+ hideTestStart = () => {
+ this.setState({
+ showTestStart: false,
+ });
+ }
+
+ showIndividualTestPrompt = async (mode) => {
+ if (!this.state.loading) {
+ if (mode === "classic") {
+ this.setState({
+ loading: true,
+ })
+ const setIds = Object.keys(this.state.selections)
+ .filter(x => this.state.selections[x]);
+
+ const totalTestQuestions = (await Promise.all(setIds.map((setId) =>
+ this.state.db.collection("sets")
+ .doc(setId)
+ .collection("vocab")
+ .get()
+ .then(querySnapshot => querySnapshot.docs.length)
+ ))).reduce((a, b) => a + b);
+
+ this.setState({
+ showTestStart: false,
+ showClassicTestStart: true,
+ sliderValue: totalTestQuestions,
+ switchLanguage: false,
+ totalTestQuestions: totalTestQuestions,
+ loading: false,
+ });
+ } else if (mode === "lives") {
+ this.setState({
+ showTestStart: false,
+ showLivesTestStart: true,
+ switchLanguage: false,
+ sliderValue: 5,
+ });
+ } else {
+ // countdown
+ // this.setState({
+ // showTestStart: false,
+ // showCountdownTestStart: true,
+ // switchLanguage: false,
+ // });
+ }
+ }
+ }
+
+ hideClassicTestStart = () => {
+ this.setState({
+ showClassicTestStart: false,
+ });
+ }
+
+ hideLivesTestStart = () => {
+ this.setState({
+ showLivesTestStart: false,
+ });
+ }
+
+ hideCountdownTestStart = () => {
+ this.setState({
+ showCountdownTestStart: false,
+ });
+ }
+
+ changeSliderValue = (value) => {
+ if (value >= 1 && value <= 999) this.setState({
+ sliderValue: value,
+ });
+ }
+
+ handleSwitchLanguageChange = (event) => {
+ this.setState({
+ switchLanguage: event.target.checked,
+ });
+ }
+
render() {
return (
@@ -251,7 +349,7 @@ export default withRouter(class SetPage extends React.Component {
{
this.state.set.public
?
-
+
:
""
}
@@ -259,7 +357,7 @@ export default withRouter(class SetPage extends React.Component {
>
}
+
+ {
+ this.state.showTestStart &&
+
+ }
+ {
+ this.state.showClassicTestStart &&
+
+ }
+ {
+ this.state.showLivesTestStart &&
+
+ }
+ {
+ this.state.showCountdownTestStart &&
+
+ }
>
}
diff --git a/src/TestStart.js b/src/TestStart.js
new file mode 100644
index 0000000..a8deee2
--- /dev/null
+++ b/src/TestStart.js
@@ -0,0 +1,36 @@
+import React from "react";
+import Loader from "./puff-loader.svg";
+
+export default function TestStart(props) {
+ return (
+ <>
+
+
+
+ {
+ props.loading
+ ?
+

+ :
+ <>
+ {
+ // ["Classic", "Lives", "Countdown"].map((mode) =>
+ ["Classic", "Lives"].map((mode) =>
+
props.showIndividualTestPrompt(mode.toLowerCase())}
+ >
+ {mode}
+
+ )
+ }
+
+ Cancel
+
+ >
+ }
+
+
+ >
+ )
+}