import * as React from "react"; import Card from "@material-ui/core/Card"; import CardContent from "@material-ui/core/CardContent"; import TextField from "@material-ui/core/TextField"; import Grid from "@material-ui/core/Grid"; import Button from "@material-ui/core/Button"; import Typography from "@material-ui/core/Typography"; import Popover from "@material-ui/core/Popover"; import LinearProgress from "@material-ui/core/LinearProgress"; import CircularProgress from "@material-ui/core/CircularProgress"; import Paper from "@material-ui/core/Paper"; import Tooltip from "@material-ui/core/Tooltip"; import Dialog from "@material-ui/core/Dialog"; import DialogActions from "@material-ui/core/DialogActions"; import DialogContent from "@material-ui/core/DialogContent"; import DialogContentText from "@material-ui/core/DialogContentText"; import DialogTitle from "@material-ui/core/DialogTitle"; import CloseIcon from "@material-ui/icons/Close"; import { withRouter } from "react-router-dom"; import { IVocab, IReviewCard, vocabToReviewCard, reviewQTypeToStr } from "../models/vocab"; import { ReviewType, IReviewMetadata } from "../models/review"; import { levW } from "../algorithms/levenshtein"; import { LEVENSHTEIN_MAX_DISTANCE } from "../config"; import { Queue } from "../utils/queue"; interface IProps { levelId?: number; vocabByLevel?: (level: number) => Promise; vocabByQueue?: () => Promise; reviewType: ReviewType; history: any; dialogOpen: boolean; loading: boolean; vocab: IVocab[]; current: IReviewCard; popoverOpen: boolean; popoverText: string; popoverColor: string; popoverTextColor: string; setReviewDialog: (state: boolean) => void; setSummary: (state: boolean) => void; setPopover: (state: boolean, text: string, color: string, textColor: string) => void; drawerButtonState: (state: boolean) => void; setLastReview: (meta: IReviewMetadata) => void; setReview: (curent: IReviewCard, meta: IReviewMetadata) => void; setLoading: (state: boolean) => void; } const ReviewPageWithRouter = withRouter( class ReviewPage extends React.Component { private vocab: IVocab[] = []; private reviewQueue: Queue = new Queue(); // Used for positioning the popover private buttonRef: HTMLButtonElement; private inputRef: HTMLInputElement; private metadata: IReviewMetadata = { correct: 0, wrong: 0 }; constructor(props: any) { super(props); // Hide the drawer button this.props.drawerButtonState(false); const vocToQueue = () => { this.vocab.forEach((vocab) => { vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue); }); this.props.setReview(this.reviewQueue.dequeue(), { correct: 0, wrong: 0, }); this.props.setLoading(false); }; // Get the correct vocabulary const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props; // Just to make TSC shut up const noopPromise = () => { return new Promise((res, rej) => { rej([]); }); }; const vocabByLevelW = vocabByLevel || noopPromise; const vocabByQueueW = vocabByQueue || noopPromise; const getVocab = { [ReviewType.LEVEL]: () => vocabByLevelW(levelId), [ReviewType.QUEUE]: () => vocabByQueueW(), }[reviewType]; getVocab().then((res: IVocab[]) => { this.vocab = res; vocToQueue(); }); } openDialog = () => { this.props.setReviewDialog(true); } closeDialog = () => { this.props.setReviewDialog(false); } cancelReview = () => { this.closeDialog(); // Show the drawer button again this.props.drawerButtonState(true); this.props.history.push("/dashboard"); } increaseMeta = (correct: number, wrong: number): IReviewMetadata => { const { metadata } = this; return { wrong: metadata.wrong + wrong, correct: metadata.correct + correct, }; } vocabFromId = (id: number): IVocab | {} => { 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.setLoading(true); // Show the drawer button again this.props.drawerButtonState(true); 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) { this.props.setPopover(true, "Das war fast richtig", "yellow", "black"); } 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", "white"); } // TODO(?): Show a snackbar for showing the updated score } render() { if (this.props.loading) { return
; } const { question, qtype } = this.props.current; const questionTitle = `${question} (${reviewQTypeToStr(qtype)})`; // TODO: const progress = 50; return
{questionTitle}
this.inputRef = node} onKeyPress={(ev) => { // Allow checking of the answer by pressing Enter if (ev.key === "Enter") this.checkInput(); }} /> this.props.setPopover(false, "", "", "")} PaperProps={{ style: { backgroundColor: this.props.popoverColor, padding: 10, color: this.props.popoverTextColor, } }}> {this.props.popoverText}
Willst du die Wiederholung abbrechen? Wenn du jetzt abbricht, dann geht dein in dieser Wiederholung gesammelte Fortschritt verloren.
; } } ); export default ReviewPageWithRouter;