feat: Show training wheels
This commit is contained in:
parent
4ba879c531
commit
7fc1d8a058
@ -200,3 +200,11 @@ export function setLevelListSnackbar(state: boolean) {
|
|||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const REVIEW_SET_HELP = "REVIEW_SET_HELP";
|
||||||
|
export function setReviewHelp(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: REVIEW_SET_HELP,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -6,3 +6,6 @@ export const BACKEND_URL = "https://192.168.178.100";
|
|||||||
|
|
||||||
// How often a wrongly answered group will be readded to the review queue
|
// How often a wrongly answered group will be readded to the review queue
|
||||||
export const MAX_ERROR_THRESHOLD = 2;
|
export const MAX_ERROR_THRESHOLD = 2;
|
||||||
|
|
||||||
|
// After how many failed attempts should we help the user
|
||||||
|
export const REVIEW_HELP_MOD = 3;
|
||||||
|
@ -2,7 +2,7 @@ import { connect } from "react-redux";
|
|||||||
|
|
||||||
import {
|
import {
|
||||||
setDrawerButton, setReviewPopover, setReviewSummary,
|
setDrawerButton, setReviewPopover, setReviewSummary,
|
||||||
setReview, setReviewLoading, setReviewDialog
|
setReview, setReviewLoading, setReviewDialog, setReviewHelp
|
||||||
} from "../actions";
|
} from "../actions";
|
||||||
|
|
||||||
import { IReviewMetadata } from "../models/review";
|
import { IReviewMetadata } from "../models/review";
|
||||||
@ -20,6 +20,7 @@ const mapStateToProps = state => {
|
|||||||
popoverColor: state.review.popoverColor,
|
popoverColor: state.review.popoverColor,
|
||||||
popoverTextColor: state.review.popoverTextColor,
|
popoverTextColor: state.review.popoverTextColor,
|
||||||
loading: state.review.loading,
|
loading: state.review.loading,
|
||||||
|
showHelp: state.review.showHelp,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
const mapDispatchToProps = dispatch => {
|
const mapDispatchToProps = dispatch => {
|
||||||
@ -30,6 +31,7 @@ const mapDispatchToProps = dispatch => {
|
|||||||
setReview: (current: IVocab, meta: IReviewMetadata) => dispatch(setReview(current, meta)),
|
setReview: (current: IVocab, meta: IReviewMetadata) => dispatch(setReview(current, meta)),
|
||||||
setLoading: (state: boolean) => dispatch(setReviewLoading(state)),
|
setLoading: (state: boolean) => dispatch(setReviewLoading(state)),
|
||||||
setReviewDialog: (state: boolean) => dispatch(setReviewDialog(state)),
|
setReviewDialog: (state: boolean) => dispatch(setReviewDialog(state)),
|
||||||
|
setShowHelp: (state: boolean) => dispatch(setReviewHelp(state)),
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -27,7 +27,10 @@ import {
|
|||||||
import { ReviewType, IReviewMetadata } from "../models/review";
|
import { ReviewType, IReviewMetadata } from "../models/review";
|
||||||
|
|
||||||
import { levW } from "../algorithms/levenshtein";
|
import { levW } from "../algorithms/levenshtein";
|
||||||
import { LEVENSHTEIN_MAX_DISTANCE, MAX_ERROR_THRESHOLD } from "../config";
|
import {
|
||||||
|
LEVENSHTEIN_MAX_DISTANCE, MAX_ERROR_THRESHOLD,
|
||||||
|
REVIEW_HELP_MOD
|
||||||
|
} from "../config";
|
||||||
|
|
||||||
import { Queue } from "../utils/queue";
|
import { Queue } from "../utils/queue";
|
||||||
|
|
||||||
@ -49,6 +52,7 @@ interface IProps {
|
|||||||
popoverText: string;
|
popoverText: string;
|
||||||
popoverColor: string;
|
popoverColor: string;
|
||||||
popoverTextColor: string;
|
popoverTextColor: string;
|
||||||
|
showHelp: boolean;
|
||||||
|
|
||||||
setReviewDialog: (state: boolean) => void;
|
setReviewDialog: (state: boolean) => void;
|
||||||
setSummary: (state: boolean) => void;
|
setSummary: (state: boolean) => void;
|
||||||
@ -56,6 +60,7 @@ interface IProps {
|
|||||||
drawerButtonState: (state: boolean) => void;
|
drawerButtonState: (state: boolean) => void;
|
||||||
setReview: (curent: IReviewCard, meta: IReviewMetadata) => void;
|
setReview: (curent: IReviewCard, meta: IReviewMetadata) => void;
|
||||||
setLoading: (state: boolean) => void;
|
setLoading: (state: boolean) => void;
|
||||||
|
setShowHelp: (state: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const ReviewPageWithRouter = withRouter(
|
const ReviewPageWithRouter = withRouter(
|
||||||
@ -132,6 +137,10 @@ const ReviewPageWithRouter = withRouter(
|
|||||||
this.props.history.push("/dashboard");
|
this.props.history.push("/dashboard");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
closeHelp = () => {
|
||||||
|
this.props.setShowHelp(false);
|
||||||
|
}
|
||||||
|
|
||||||
increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
|
increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
|
||||||
const { metadata } = this.props;
|
const { metadata } = this.props;
|
||||||
|
|
||||||
@ -239,6 +248,9 @@ const ReviewPageWithRouter = withRouter(
|
|||||||
// NOTE: We don't need to force a re-render, as the state
|
// NOTE: We don't need to force a re-render, as the state
|
||||||
// will be updated since we need to show the popover.
|
// will be updated since we need to show the popover.
|
||||||
vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
|
vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
|
||||||
|
} else if (this.error_data[current.id] % REVIEW_HELP_MOD === 0) {
|
||||||
|
// Help the user
|
||||||
|
this.props.setShowHelp(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
this.props.setReview(this.props.current, this.increaseMeta(0, 1));
|
this.props.setReview(this.props.current, this.increaseMeta(0, 1));
|
||||||
@ -246,6 +258,101 @@ const ReviewPageWithRouter = withRouter(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
vocabSpecificInformation(vocab: IVocab) {
|
||||||
|
switch (vocab.type) {
|
||||||
|
case VocabType.NOMEN:
|
||||||
|
const nData = vocab.latin as INomenData;
|
||||||
|
return <div>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Genitiv:</b> {nData.genitiv}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Genus:</b> {nData.genus}
|
||||||
|
</Typography>
|
||||||
|
</div>;
|
||||||
|
case VocabType.VERB:
|
||||||
|
const vData = vocab.latin as IVerbData;
|
||||||
|
return <div>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>1. Person Präsens:</b> {vData.praesens}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>1. Person Perfekt:</b> {vData.perfekt}
|
||||||
|
</Typography>
|
||||||
|
</div>;
|
||||||
|
case VocabType.ADJEKTIV:
|
||||||
|
const aData = vocab.latin as IAdjektivData;
|
||||||
|
return <div>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Endung feminin:</b> {aData.endung_f}
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Endung neutrum:</b> {aData.endung_n}
|
||||||
|
</Typography>
|
||||||
|
</div>;
|
||||||
|
case VocabType.ADVERB:
|
||||||
|
return <div />;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
helpDialog = () => {
|
||||||
|
// Find the vocabulary
|
||||||
|
// TODO: if (!vocab)
|
||||||
|
const vocab = this.vocab.find(el => el.id === this.props.current.id) as IVocab;
|
||||||
|
|
||||||
|
// TODO: This should be shared with LevelPage
|
||||||
|
return <Dialog
|
||||||
|
open={this.props.showHelp}
|
||||||
|
onClose={this.closeHelp}>
|
||||||
|
<DialogTitle>Wiederholung von {vocab.latin.grundform}</DialogTitle>
|
||||||
|
<DialogContent>
|
||||||
|
<Typography gutterBottom variant="headline" component="h3">
|
||||||
|
{vocab.german.join(", ")}
|
||||||
|
</Typography>
|
||||||
|
{this.vocabSpecificInformation(vocab)}
|
||||||
|
{
|
||||||
|
vocab.hint ? (
|
||||||
|
<div style={{
|
||||||
|
border: "dashed",
|
||||||
|
borderColor: "red",
|
||||||
|
padding: 12,
|
||||||
|
}}>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Tipp:</b>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{vocab.hint}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
{
|
||||||
|
vocab.mnemonic ? (
|
||||||
|
<div style={{
|
||||||
|
border: "dashed",
|
||||||
|
borderColor: "#f1c40f",
|
||||||
|
marginTop: 12,
|
||||||
|
padding: 12,
|
||||||
|
}}>
|
||||||
|
<Typography variant="subheading" component="p">
|
||||||
|
<b>Eselsbrücke:</b>
|
||||||
|
</Typography>
|
||||||
|
<Typography variant="body2">
|
||||||
|
{vocab.mnemonic}
|
||||||
|
</Typography>
|
||||||
|
</div>
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={this.closeHelp}>
|
||||||
|
Zurück zur Wiederholung
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog>;
|
||||||
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
if (this.props.loading) {
|
if (this.props.loading) {
|
||||||
return <div>
|
return <div>
|
||||||
@ -368,6 +475,11 @@ const ReviewPageWithRouter = withRouter(
|
|||||||
<CloseIcon />
|
<CloseIcon />
|
||||||
</Button>
|
</Button>
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
|
{
|
||||||
|
this.props.showHelp ? (
|
||||||
|
this.helpDialog()
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
<Dialog
|
<Dialog
|
||||||
open={this.props.dialogOpen}
|
open={this.props.dialogOpen}
|
||||||
onClose={this.closeDialog}>
|
onClose={this.closeDialog}>
|
||||||
|
@ -52,6 +52,7 @@ interface IState {
|
|||||||
popoverText: string;
|
popoverText: string;
|
||||||
popoverColor: string;
|
popoverColor: string;
|
||||||
popoverTextColor: string;
|
popoverTextColor: string;
|
||||||
|
showHelp: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
topTen: ILearner[];
|
topTen: ILearner[];
|
||||||
@ -110,6 +111,7 @@ const initialState: IState = {
|
|||||||
popoverText: "",
|
popoverText: "",
|
||||||
popoverColor: "",
|
popoverColor: "",
|
||||||
popoverTextColor: "",
|
popoverTextColor: "",
|
||||||
|
showHelp: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
nextLevel: {} as ILevel,
|
nextLevel: {} as ILevel,
|
||||||
@ -256,6 +258,12 @@ export function LateinicusApp(state: IState = initialState, action: any) {
|
|||||||
snackbar: action.state,
|
snackbar: action.state,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
case Actions.REVIEW_SET_HELP:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
review: Object.assign({}, state.review, {
|
||||||
|
showHelp: action.state,
|
||||||
|
}),
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
// Ignore the initialization call to the reducer. By that we can
|
// Ignore the initialization call to the reducer. By that we can
|
||||||
// catch all actions that are not implemented
|
// catch all actions that are not implemented
|
||||||
|
Reference in New Issue
Block a user