fix: Go to the summary page

This commit is contained in:
Alexander Polynomdivision 2018-09-19 17:25:54 +02:00
parent 882ca5a9e3
commit 2dc9aa3a53
3 changed files with 189 additions and 218 deletions

View File

@ -110,14 +110,6 @@ export function setReviewPopover(state: boolean, text: string, color: string) {
}; };
}; };
export const REVIEW_SET_SUMMARY = "REVIEW_SET_SUMMARY";
export function setReviewSummary(state: boolean) {
return {
type: REVIEW_SET_SUMMARY,
state,
};
};
export const REVIEW_SET_LOADING = "REVIEW_SET_LOADING"; export const REVIEW_SET_LOADING = "REVIEW_SET_LOADING";
export function setReviewLoading(state: boolean) { export function setReviewLoading(state: boolean) {
return { return {

View File

@ -11,7 +11,7 @@ import LinearProgress from "@material-ui/core/LinearProgress";
import CircularProgress from "@material-ui/core/CircularProgress"; import CircularProgress from "@material-ui/core/CircularProgress";
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper";
import { Redirect } from "react-router-dom"; import { withRouter } from "react-router-dom";
import { IVocab, IReviewCard, vocabToReviewCard, reviewQTypeToStr } from "../models/vocab"; import { IVocab, IReviewCard, vocabToReviewCard, reviewQTypeToStr } from "../models/vocab";
import { ReviewType, IReviewMetadata } from "../models/review"; import { ReviewType, IReviewMetadata } from "../models/review";
@ -26,12 +26,13 @@ interface IProps {
vocabByLevel?: (level: number) => Promise<IVocab[]>; vocabByLevel?: (level: number) => Promise<IVocab[]>;
vocabByQueue?: () => Promise<IVocab[]>; vocabByQueue?: () => Promise<IVocab[]>;
history: any;
reviewType: ReviewType; reviewType: ReviewType;
loading: boolean; loading: boolean;
vocab: IVocab[]; vocab: IVocab[];
current: IReviewCard; current: IReviewCard;
toSummary: boolean;
popoverOpen: boolean; popoverOpen: boolean;
popoverText: string; popoverText: string;
popoverColor: string; popoverColor: string;
@ -44,219 +45,205 @@ interface IProps {
setLoading: (state: boolean) => void; setLoading: (state: boolean) => void;
} }
export default class ReviewPage extends React.Component<IProps> { const ReviewPageWithRouter = withRouter(
private vocab: IVocab[] = []; class ReviewPage extends React.Component<IProps> {
private reviewQueue: Queue<IReviewCard> = new Queue(); private vocab: IVocab[] = [];
// Used for positioning the popover private reviewQueue: Queue<IReviewCard> = new Queue();
private buttonRef: HTMLButtonElement; // Used for positioning the popover
private inputRef: HTMLInputElement; private buttonRef: HTMLButtonElement;
private metadata: IReviewMetadata = { correct: 0, wrong: 0 }; private inputRef: HTMLInputElement;
private metadata: IReviewMetadata = { correct: 0, wrong: 0 };
constructor(props: any) { constructor(props: any) {
super(props); super(props);
// Hide the drawer button // Hide the drawer button
this.props.drawerButtonState(false); this.props.drawerButtonState(false);
const vocToQueue = () => { const vocToQueue = () => {
this.vocab.forEach((vocab) => { this.vocab.forEach((vocab) => {
vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue); vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
}); });
this.props.setReview(this.reviewQueue.dequeue(), { this.props.setReview(this.reviewQueue.dequeue(), {
correct: 0, correct: 0,
wrong: 0, wrong: 0,
}); });
this.props.setLoading(false); this.props.setLoading(false);
}; };
// Get the correct vocabulary // Get the correct vocabulary
const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props; const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props;
switch (reviewType) { switch (reviewType) {
case ReviewType.LEVEL: case ReviewType.LEVEL:
if (!vocabByLevel || !levelId) { if (!vocabByLevel || !levelId) {
alert("[ReviewPage::constructor] vocabByLevel or levelId undefined"); alert("[ReviewPage::constructor] vocabByLevel or levelId undefined");
} else { } else {
vocabByLevel(levelId).then(res => { vocabByLevel(levelId).then(res => {
this.vocab = res; this.vocab = res;
vocToQueue(); vocToQueue();
}); });
} }
break; break;
case ReviewType.QUEUE: case ReviewType.QUEUE:
if (!vocabByQueue) { if (!vocabByQueue) {
alert("[ReviewPage::constructor] vocabByQueue undefined"); alert("[ReviewPage::constructor] vocabByQueue undefined");
} else { } else {
vocabByQueue().then(res => { vocabByQueue().then(res => {
this.vocab = res; this.vocab = res;
vocToQueue(); vocToQueue();
}); });
} }
break; break;
}
}
increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
const { metadata } = this;
return {
wrong: metadata.wrong + wrong,
correct: metadata.correct + correct,
};
}
vocabFromId = (id: number) => {
return this.vocab.find((el) => el.id === this.props.current.id);
}
checkInput = () => {
// Check if the given answer is somewhere in the german words
const input = this.inputRef.value || "";
// Map all possible answers to lowercase ( => ignore casing)
const answers = this.props.current.answers.map((el) => el.toLowerCase());
// Calculate the distances to all possible answers
const dists = answers.map((el) => levW(input.toLowerCase(), el));
// Find the lowest distance
const minDist = Math.min(...dists);
console.log("Review Queue size:", this.reviewQueue.size());
// Check if the user's answer was correct
if (minDist === 0) {
// TODO: Show it's correct?
// Show the next vocab word
if (this.reviewQueue.size() === 0) {
// Go to the summary screen
this.props.setLastReview(this.metadata);
this.props.setSummary(true);
} else {
// Increase the vocab
this.props.setReview(this.reviewQueue.dequeue(), this.increaseMeta(1, 0));
this.inputRef.value = "";
} }
} else if (minDist <= LEVENSHTEIN_MAX_DISTANCE) {
// TODO: Show a hint
console.log("Partially correct");
} else {
// Find the IVocab item
const vocab = this.vocabFromId(this.props.current.id);
if (vocab) {
// Re-Add the vocabulary item to the review queue
// TODO: Only re-add when it when it's not re-queued
// vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
} else {
console.log("[ReviewPage::checkInput] Could not find IVocab item for wrong IReviewCard");
}
this.props.setPopover(true, "Das war nicht richtig", "red");
} }
// TODO(?): Show a snackbar for showing the updated score increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
} const { metadata } = this;
return {
wrong: metadata.wrong + wrong,
correct: metadata.correct + correct,
};
}
vocabFromId = (id: number) => {
return this.vocab.find((el) => el.id === this.props.current.id);
}
checkInput = () => {
// Check if the given answer is somewhere in the german words
const input = this.inputRef.value || "";
// Map all possible answers to lowercase ( => ignore casing)
const answers = this.props.current.answers.map((el) => el.toLowerCase());
// Calculate the distances to all possible answers
const dists = answers.map((el) => levW(input.toLowerCase(), el));
// Find the lowest distance
const minDist = Math.min(...dists);
console.log("Review Queue size:", this.reviewQueue.size());
// Check if the user's answer was correct
if (minDist === 0) {
// TODO: Show it's correct?
// Show the next vocab word
if (this.reviewQueue.size() === 0) {
// Go to the summary screen
this.props.setLastReview(this.metadata);
this.props.history.push("/review/summary");
} else {
// Increase the vocab
this.props.setReview(this.reviewQueue.dequeue(), this.increaseMeta(1, 0));
this.inputRef.value = "";
}
} else if (minDist <= LEVENSHTEIN_MAX_DISTANCE) {
// TODO: Show a hint
console.log("Partially correct");
} else {
// Find the IVocab item
const vocab = this.vocabFromId(this.props.current.id);
if (vocab) {
// Re-Add the vocabulary item to the review queue
// TODO: Only re-add when it when it's not re-queued
// vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
} else {
console.log("[ReviewPage::checkInput] Could not find IVocab item for wrong IReviewCard");
}
this.props.setPopover(true, "Das war nicht richtig", "red");
}
// TODO(?): Show a snackbar for showing the updated score
}
render() {
if (this.props.loading) {
return <div>
<Grid
container
spacing={0}
direction="column"
alignItems="center"
justify="center"
style={{ minHeight: '100vh' }}>
<Grid item xs={12}>
<Paper className="paper">
<Grid container direction="column" spacing={8}>
<CircularProgress />
</Grid>
</Paper>
</Grid>
</Grid>
</div>;
}
const { question, qtype } = this.props.current;
const questionTitle = `${question} (${reviewQTypeToStr(qtype)})`;
// TODO:
const progress = 50;
render() {
if (this.props.loading) {
return <div> return <div>
{/* <Grid container justify="center">
* This would be the case when the user presses the "to <Grid item style={{ width: "100%" }}>
* review" button. That is because we need the state of loading <Card>
* to be true, when this page gets called <CardContent>
* TODO:? <Grid container direction="column">
*/} <Typography variant="display2">
{ {questionTitle}
this.props.toSummary ? ( </Typography>
<Redirect to="/review/summary" /> <TextField
) : undefined margin="normal"
} fullWidth={true}
inputRef={node => this.inputRef = node}
<Grid onKeyPress={(ev) => {
container // Allow checking of the answer by pressing Enter
spacing={0} if (ev.key === "Enter")
direction="column" this.checkInput();
alignItems="center" }} />
justify="center" <LinearProgress
style={{ minHeight: '100vh' }}> variant="determinate"
<Grid item xs={12}> value={progress} />
<Paper className="paper"> <Popover
<Grid container direction="column" spacing={8}> open={this.props.popoverOpen}
<CircularProgress /> anchorOrigin={{
</Grid> vertical: "center",
</Paper> horizontal: "center"
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center"
}}
anchorEl={this.buttonRef}
onClose={() => this.props.setPopover(false, "", "")}
PaperProps={{
style: {
backgroundColor: this.props.popoverColor,
padding: 10,
color: "white"
}
}}>
<Typography
variant="button"
color="inherit">
{this.props.popoverText}
</Typography>
</Popover>
<Button
onClick={this.checkInput}
buttonRef={node => this.buttonRef = node}>
Prüfen
</Button>
</Grid>
</CardContent>
</Card>
</Grid> </Grid>
</Grid> </Grid>
</div>; </div>;
} }
const { question, qtype } = this.props.current;
const questionTitle = `${question} (${reviewQTypeToStr(qtype)})`;
// TODO:
const progress = 50;
return <div>
{
this.props.toSummary ? (
<Redirect to="/review/summary" />
) : undefined
}
<Grid container justify="center">
<Grid item style={{ width: "100%" }}>
<Card>
<CardContent>
<Grid container direction="column">
<Typography variant="display2">
{questionTitle}
</Typography>
<TextField
margin="normal"
fullWidth={true}
inputRef={node => this.inputRef = node}
onKeyPress={(ev) => {
// Allow checking of the answer by pressing Enter
if (ev.key === "Enter")
this.checkInput();
}} />
<LinearProgress
variant="determinate"
value={progress} />
<Popover
open={this.props.popoverOpen}
anchorOrigin={{
vertical: "center",
horizontal: "center"
}}
transformOrigin={{
vertical: "bottom",
horizontal: "center"
}}
anchorEl={this.buttonRef}
onClose={() => this.props.setPopover(false, "", "")}
PaperProps={{
style: {
backgroundColor: this.props.popoverColor,
padding: 10,
color: "white"
}
}}>
<Typography
variant="button"
color="inherit">
{this.props.popoverText}
</Typography>
</Popover>
<Button
onClick={this.checkInput}
buttonRef={node => this.buttonRef = node}>
Prüfen
</Button>
</Grid>
</CardContent>
</Card>
</Grid>
</Grid>
</div>;
} }
}; );
export default ReviewPageWithRouter;

View File

@ -37,7 +37,6 @@ interface IState {
loading: boolean; loading: boolean;
vocab: IVocab[]; vocab: IVocab[];
metadata: IReviewMetadata; metadata: IReviewMetadata;
toSummary: boolean;
popoverOpen: boolean; popoverOpen: boolean;
popoverText: string; popoverText: string;
popoverColor: string; popoverColor: string;
@ -82,7 +81,6 @@ const initialState: IState = {
loading: true, loading: true,
vocab: [], vocab: [],
metadata: {} as IReviewMetadata, metadata: {} as IReviewMetadata,
toSummary: false,
popoverOpen: false, popoverOpen: false,
popoverText: "", popoverText: "",
popoverColor: "", popoverColor: "",
@ -193,12 +191,6 @@ export function LateinicusApp(state: IState = initialState, action: any) {
loading: action.state, loading: action.state,
}), }),
}); });
case Actions.REVIEW_SET_SUMMARY:
return Object.assign({}, state, {
review: Object.assign({}, state.review, {
toSummary: action.state,
}),
});
default: default:
if (action.type) { if (action.type) {
console.log("Reducer not implemented:", action.type); console.log("Reducer not implemented:", action.type);