feat: Partially transition Review to Redux
This commit is contained in:
parent
d94fa63ac7
commit
882ca5a9e3
@ -1,5 +1,7 @@
|
|||||||
import { IVocab } from "../models/vocab";
|
import { IVocab } from "../models/vocab";
|
||||||
import { IUser } from "../models/user";
|
import { IUser } from "../models/user";
|
||||||
|
import { ILevel } from "../models/level";
|
||||||
|
import { IReviewMetadata, IReviewCard } from "../models/review";
|
||||||
|
|
||||||
export const SET_DRAWER = "SET_DRAWER";
|
export const SET_DRAWER = "SET_DRAWER";
|
||||||
export function setDrawer(state: boolean) {
|
export function setDrawer(state: boolean) {
|
||||||
@ -89,3 +91,54 @@ export function setLevelLoading(state: boolean) {
|
|||||||
state,
|
state,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const SET_LEVELS = "SET_LEVELS";
|
||||||
|
export function setLevels(levels: ILevel[]) {
|
||||||
|
return {
|
||||||
|
type: SET_LEVELS,
|
||||||
|
levels,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const REVIEW_SET_POPOVER = "REVIEW_SET_POPOVER";
|
||||||
|
export function setReviewPopover(state: boolean, text: string, color: string) {
|
||||||
|
return {
|
||||||
|
type: REVIEW_SET_POPOVER,
|
||||||
|
state,
|
||||||
|
text,
|
||||||
|
color,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
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 function setReviewLoading(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: REVIEW_SET_LOADING,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SET_LAST_REVIEW = "SET_LAST_REVIEW";
|
||||||
|
export function setLastReview(metadata: IReviewMetadata) {
|
||||||
|
return {
|
||||||
|
type: SET_LAST_REVIEW,
|
||||||
|
metadata,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SET_REVIEW = "SET_REVIEW";
|
||||||
|
export function setReview(current: IReviewCard, meta: IReviewMetadata) {
|
||||||
|
return {
|
||||||
|
type: SET_REVIEW,
|
||||||
|
current,
|
||||||
|
meta,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -6,9 +6,9 @@ import AuthRoute from "../security/AuthRoute";
|
|||||||
|
|
||||||
import Dashboard from "../pages/dashboard";
|
import Dashboard from "../pages/dashboard";
|
||||||
import LoginPage from "../containers/LoginPage";
|
import LoginPage from "../containers/LoginPage";
|
||||||
import LevelListPage from "../pages/levelList";
|
import LevelListPage from "../containers/LevelList";
|
||||||
import LevelPage from "../containers/LevelPage";
|
import LevelPage from "../containers/LevelPage";
|
||||||
import ReviewPage from "../pages/review";
|
import ReviewPage from "../containers/Review";
|
||||||
import SummaryPage from "../pages/summary";
|
import SummaryPage from "../pages/summary";
|
||||||
import WelcomePage from "../pages/intro";
|
import WelcomePage from "../pages/intro";
|
||||||
|
|
||||||
@ -258,8 +258,7 @@ export default class Application extends React.Component<IProps> {
|
|||||||
<AuthRoute
|
<AuthRoute
|
||||||
isAuth={this.isAuthenticated}
|
isAuth={this.isAuthenticated}
|
||||||
path="/levelList"
|
path="/levelList"
|
||||||
component={() => <LevelListPage
|
component={() => <LevelListPage />} />
|
||||||
levels={this.getLevels()} />} />
|
|
||||||
{/*We cannot use AuthRoute here, because match is undefined otherwise*/}
|
{/*We cannot use AuthRoute here, because match is undefined otherwise*/}
|
||||||
<Route
|
<Route
|
||||||
path="/level/:id"
|
path="/level/:id"
|
||||||
@ -282,7 +281,6 @@ export default class Application extends React.Component<IProps> {
|
|||||||
reviewType={ReviewType.LEVEL}
|
reviewType={ReviewType.LEVEL}
|
||||||
levelId={match.params.id}
|
levelId={match.params.id}
|
||||||
vocabByLevel={this.getLevelVocab}
|
vocabByLevel={this.getLevelVocab}
|
||||||
drawerButtonState={this.drawerButtonState}
|
|
||||||
setLastReview={this.setLastReview} />;
|
setLastReview={this.setLastReview} />;
|
||||||
} else {
|
} else {
|
||||||
return <Redirect to="/login" />;
|
return <Redirect to="/login" />;
|
||||||
|
14
src/containers/LevelList.ts
Normal file
14
src/containers/LevelList.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
import LevelListPage from "../pages/levelList";
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
levels: state.levels,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const mapDispatchToProps = dispatch => { return {} };
|
||||||
|
|
||||||
|
const LevelListContainer = connect(mapStateToProps,
|
||||||
|
mapDispatchToProps)(LevelListPage);
|
||||||
|
export default LevelListContainer;
|
35
src/containers/Review.ts
Normal file
35
src/containers/Review.ts
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
import {
|
||||||
|
setDrawerButton, setReviewPopover, setReviewSummary, setLastReview,
|
||||||
|
setReview, setReviewLoading
|
||||||
|
} from "../actions";
|
||||||
|
|
||||||
|
import { IReviewMetadata } from "../models/review";
|
||||||
|
import { IVocab } from "../models/vocab";
|
||||||
|
import ReviewPage from "../pages/review";
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
return {
|
||||||
|
metadata: state.review.metadata,
|
||||||
|
vocab: state.review.vocab,
|
||||||
|
current: state.review.current,
|
||||||
|
popoverOpen: state.review.popoverOpen,
|
||||||
|
popoverText: state.review.popoverText,
|
||||||
|
popoverColor: state.review.popoverColor,
|
||||||
|
loading: state.review.loading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
drawerButtonState: (state: boolean) => dispatch(setDrawerButton(state)),
|
||||||
|
setPopover: (state: boolean, text: string, color: string) => dispatch(setReviewPopover(state, text, color)),
|
||||||
|
setSummary: (state: boolean) => dispatch(setReviewSummary(state)),
|
||||||
|
setReview: (current: IVocab, meta: IReviewMetadata) => dispatch(setReview(current, meta)),
|
||||||
|
setLoading: (state: boolean) => dispatch(setReviewLoading(state)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const ReviewContainer = connect(mapStateToProps,
|
||||||
|
mapDispatchToProps)(ReviewPage);
|
||||||
|
export default ReviewContainer;
|
@ -8,6 +8,8 @@ import Button from "@material-ui/core/Button";
|
|||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
import Popover from "@material-ui/core/Popover";
|
import Popover from "@material-ui/core/Popover";
|
||||||
import LinearProgress from "@material-ui/core/LinearProgress";
|
import LinearProgress from "@material-ui/core/LinearProgress";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
|
||||||
import { Redirect } from "react-router-dom";
|
import { Redirect } from "react-router-dom";
|
||||||
|
|
||||||
@ -21,34 +23,34 @@ import { Queue } from "../utils/queue";
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
levelId?: number;
|
levelId?: number;
|
||||||
vocabByLevel?: (level: number) => IVocab[];
|
vocabByLevel?: (level: number) => Promise<IVocab[]>;
|
||||||
vocabByQueue?: () => IVocab[];
|
vocabByQueue?: () => Promise<IVocab[]>;
|
||||||
|
|
||||||
setLastReview: (meta: IReviewMetadata) => void;
|
|
||||||
|
|
||||||
reviewType: ReviewType;
|
reviewType: ReviewType;
|
||||||
|
|
||||||
drawerButtonState: (state: boolean) => void;
|
loading: boolean;
|
||||||
}
|
vocab: IVocab[];
|
||||||
|
|
||||||
interface IState {
|
|
||||||
input: string;
|
|
||||||
current: IReviewCard;
|
current: IReviewCard;
|
||||||
|
|
||||||
metadata: IReviewMetadata;
|
|
||||||
|
|
||||||
toSummary: boolean;
|
toSummary: boolean;
|
||||||
|
|
||||||
popoverOpen: boolean;
|
popoverOpen: boolean;
|
||||||
popoverText: string;
|
popoverText: string;
|
||||||
popoverColor: string;
|
popoverColor: string;
|
||||||
|
|
||||||
|
setSummary: (state: boolean) => void;
|
||||||
|
setPopover: (state: boolean, text: string, color: string) => void;
|
||||||
|
drawerButtonState: (state: boolean) => void;
|
||||||
|
setLastReview: (meta: IReviewMetadata) => void;
|
||||||
|
setReview: (curent: IReviewCard, meta: IReviewMetadata) => void;
|
||||||
|
setLoading: (state: boolean) => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default class ReviewPage extends React.Component<IProps, IState> {
|
export default class ReviewPage extends React.Component<IProps> {
|
||||||
private vocab: IVocab[] = [];
|
private vocab: IVocab[] = [];
|
||||||
private reviewQueue: Queue<IReviewCard> = undefined;
|
private reviewQueue: Queue<IReviewCard> = new Queue();
|
||||||
// Used for positioning the popover
|
// Used for positioning the popover
|
||||||
private buttonRef: HTMLButtonElement;
|
private buttonRef: HTMLButtonElement;
|
||||||
|
private inputRef: HTMLInputElement;
|
||||||
|
private metadata: IReviewMetadata = { correct: 0, wrong: 0 };
|
||||||
|
|
||||||
constructor(props: any) {
|
constructor(props: any) {
|
||||||
super(props);
|
super(props);
|
||||||
@ -56,6 +58,17 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
// Hide the drawer button
|
// Hide the drawer button
|
||||||
this.props.drawerButtonState(false);
|
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
|
// Get the correct vocabulary
|
||||||
const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props;
|
const { reviewType, vocabByLevel, levelId, vocabByQueue } = this.props;
|
||||||
switch (reviewType) {
|
switch (reviewType) {
|
||||||
@ -63,7 +76,10 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
if (!vocabByLevel || !levelId) {
|
if (!vocabByLevel || !levelId) {
|
||||||
alert("[ReviewPage::constructor] vocabByLevel or levelId undefined");
|
alert("[ReviewPage::constructor] vocabByLevel or levelId undefined");
|
||||||
} else {
|
} else {
|
||||||
this.vocab = vocabByLevel(levelId);
|
vocabByLevel(levelId).then(res => {
|
||||||
|
this.vocab = res;
|
||||||
|
vocToQueue();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
@ -71,38 +87,18 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
if (!vocabByQueue) {
|
if (!vocabByQueue) {
|
||||||
alert("[ReviewPage::constructor] vocabByQueue undefined");
|
alert("[ReviewPage::constructor] vocabByQueue undefined");
|
||||||
} else {
|
} else {
|
||||||
this.vocab = vocabByQueue();
|
vocabByQueue().then(res => {
|
||||||
|
this.vocab = res;
|
||||||
|
vocToQueue();
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Turn the vocabulary into IReviewCards and queue them
|
|
||||||
if (!this.reviewQueue) {
|
|
||||||
this.reviewQueue = new Queue();
|
|
||||||
this.vocab.forEach((vocab) => {
|
|
||||||
vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
this.state = {
|
|
||||||
input: "",
|
|
||||||
current: this.reviewQueue.dequeue(),
|
|
||||||
metadata: {
|
|
||||||
correct: 0,
|
|
||||||
wrong: 0,
|
|
||||||
},
|
|
||||||
|
|
||||||
toSummary: false,
|
|
||||||
|
|
||||||
popoverOpen: false,
|
|
||||||
popoverText: "",
|
|
||||||
popoverColor: "red",
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
|
increaseMeta = (correct: number, wrong: number): IReviewMetadata => {
|
||||||
const { metadata } = this.state;
|
const { metadata } = this;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
wrong: metadata.wrong + wrong,
|
wrong: metadata.wrong + wrong,
|
||||||
@ -111,21 +107,21 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
vocabFromId = (id: number) => {
|
vocabFromId = (id: number) => {
|
||||||
return this.vocab.find((el) => el.id === this.state.current.id);
|
return this.vocab.find((el) => el.id === this.props.current.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
checkInput = () => {
|
checkInput = () => {
|
||||||
// Check if the given answer is somewhere in the german words
|
// Check if the given answer is somewhere in the german words
|
||||||
const { input } = this.state;
|
const input = this.inputRef.value || "";
|
||||||
|
|
||||||
// Map all possible answers to lowercase ( => ignore casing)
|
// Map all possible answers to lowercase ( => ignore casing)
|
||||||
const answers = this.state.current.answers.map((el) => el.toLowerCase());
|
const answers = this.props.current.answers.map((el) => el.toLowerCase());
|
||||||
// Calculate the distances to all possible answers
|
// Calculate the distances to all possible answers
|
||||||
const dists = answers.map((el) => levW(input.toLowerCase(), el));
|
const dists = answers.map((el) => levW(input.toLowerCase(), el));
|
||||||
// Find the lowest distance
|
// Find the lowest distance
|
||||||
const minDist = Math.min(...dists);
|
const minDist = Math.min(...dists);
|
||||||
|
|
||||||
console.log(this.reviewQueue.size());
|
console.log("Review Queue size:", this.reviewQueue.size());
|
||||||
|
|
||||||
// Check if the user's answer was correct
|
// Check if the user's answer was correct
|
||||||
if (minDist === 0) {
|
if (minDist === 0) {
|
||||||
@ -133,57 +129,76 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
// Show the next vocab word
|
// Show the next vocab word
|
||||||
if (this.reviewQueue.size() === 0) {
|
if (this.reviewQueue.size() === 0) {
|
||||||
// Go to the summary screen
|
// Go to the summary screen
|
||||||
this.setState({
|
this.props.setLastReview(this.metadata);
|
||||||
toSummary: true,
|
this.props.setSummary(true);
|
||||||
}, () => {
|
|
||||||
// Update the "Last Review" data
|
|
||||||
this.props.setLastReview(this.state.metadata);
|
|
||||||
});
|
|
||||||
} else {
|
} else {
|
||||||
// Increase the vocab
|
// Increase the vocab
|
||||||
this.setState({
|
this.props.setReview(this.reviewQueue.dequeue(), this.increaseMeta(1, 0));
|
||||||
current: this.reviewQueue.dequeue(),
|
this.inputRef.value = "";
|
||||||
input: "",
|
|
||||||
// Add one correct answer
|
|
||||||
metadata: this.increaseMeta(1, 0),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
} else if (minDist <= LEVENSHTEIN_MAX_DISTANCE) {
|
} else if (minDist <= LEVENSHTEIN_MAX_DISTANCE) {
|
||||||
// TODO: Show a hint
|
// TODO: Show a hint
|
||||||
console.log("Partially correct");
|
console.log("Partially correct");
|
||||||
} else {
|
} else {
|
||||||
// Find the IVocab item
|
// Find the IVocab item
|
||||||
const vocab = this.vocabFromId(this.state.current.id);
|
const vocab = this.vocabFromId(this.props.current.id);
|
||||||
if (vocab) {
|
if (vocab) {
|
||||||
// Re-Add the vocabulary item to the review queue
|
// Re-Add the vocabulary item to the review queue
|
||||||
// TODO: Only re-add when it when it's not re-queued
|
// TODO: Only re-add when it when it's not re-queued
|
||||||
vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
|
// vocabToReviewCard(vocab).forEach(this.reviewQueue.enqueue);
|
||||||
} else {
|
} else {
|
||||||
console.log("[ReviewPage::checkInput] Could not find IVocab item for wrong IReviewCard");
|
console.log("[ReviewPage::checkInput] Could not find IVocab item for wrong IReviewCard");
|
||||||
}
|
}
|
||||||
|
|
||||||
this.setState({
|
this.props.setPopover(true, "Das war nicht richtig", "red");
|
||||||
popoverOpen: true,
|
|
||||||
popoverText: "Das war nicht richtig",
|
|
||||||
popoverColor: "red",
|
|
||||||
// TODO: Or maybe don't reset the text
|
|
||||||
input: "",
|
|
||||||
metadata: this.increaseMeta(0, 1),
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(?): Show a snackbar for showing the updated score
|
// TODO(?): Show a snackbar for showing the updated score
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { question, qtype } = this.state.current;
|
if (this.props.loading) {
|
||||||
|
return <div>
|
||||||
|
{/*
|
||||||
|
* This would be the case when the user presses the "to
|
||||||
|
* review" button. That is because we need the state of loading
|
||||||
|
* to be true, when this page gets called
|
||||||
|
* TODO:?
|
||||||
|
*/}
|
||||||
|
{
|
||||||
|
this.props.toSummary ? (
|
||||||
|
<Redirect to="/review/summary" />
|
||||||
|
) : undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
<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)})`;
|
const questionTitle = `${question} (${reviewQTypeToStr(qtype)})`;
|
||||||
// TODO:
|
// TODO:
|
||||||
const progress = 50;
|
const progress = 50;
|
||||||
|
|
||||||
return <div>
|
return <div>
|
||||||
{
|
{
|
||||||
this.state.toSummary ? (
|
this.props.toSummary ? (
|
||||||
<Redirect to="/review/summary" />
|
<Redirect to="/review/summary" />
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
@ -198,10 +213,7 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
<TextField
|
<TextField
|
||||||
margin="normal"
|
margin="normal"
|
||||||
fullWidth={true}
|
fullWidth={true}
|
||||||
value={this.state.input}
|
inputRef={node => this.inputRef = node}
|
||||||
onChange={(ev) => this.setState({
|
|
||||||
input: ev.target.value,
|
|
||||||
})}
|
|
||||||
onKeyPress={(ev) => {
|
onKeyPress={(ev) => {
|
||||||
// Allow checking of the answer by pressing Enter
|
// Allow checking of the answer by pressing Enter
|
||||||
if (ev.key === "Enter")
|
if (ev.key === "Enter")
|
||||||
@ -211,7 +223,7 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
variant="determinate"
|
variant="determinate"
|
||||||
value={progress} />
|
value={progress} />
|
||||||
<Popover
|
<Popover
|
||||||
open={this.state.popoverOpen}
|
open={this.props.popoverOpen}
|
||||||
anchorOrigin={{
|
anchorOrigin={{
|
||||||
vertical: "center",
|
vertical: "center",
|
||||||
horizontal: "center"
|
horizontal: "center"
|
||||||
@ -221,12 +233,10 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
horizontal: "center"
|
horizontal: "center"
|
||||||
}}
|
}}
|
||||||
anchorEl={this.buttonRef}
|
anchorEl={this.buttonRef}
|
||||||
onClose={() => this.setState({
|
onClose={() => this.props.setPopover(false, "", "")}
|
||||||
popoverOpen: false,
|
|
||||||
})}
|
|
||||||
PaperProps={{
|
PaperProps={{
|
||||||
style: {
|
style: {
|
||||||
backgroundColor: this.state.popoverColor,
|
backgroundColor: this.props.popoverColor,
|
||||||
padding: 10,
|
padding: 10,
|
||||||
color: "white"
|
color: "white"
|
||||||
}
|
}
|
||||||
@ -234,7 +244,7 @@ export default class ReviewPage extends React.Component<IProps, IState> {
|
|||||||
<Typography
|
<Typography
|
||||||
variant="button"
|
variant="button"
|
||||||
color="inherit">
|
color="inherit">
|
||||||
{this.state.popoverText}
|
{this.props.popoverText}
|
||||||
</Typography>
|
</Typography>
|
||||||
</Popover>
|
</Popover>
|
||||||
<Button
|
<Button
|
||||||
|
@ -1,8 +1,10 @@
|
|||||||
import * as Actions from "../actions";
|
import * as Actions from "../actions";
|
||||||
|
|
||||||
import { ILearner } from "../models/learner";
|
import { ILearner } from "../models/learner";
|
||||||
|
import { ILevel } from "../models/level";
|
||||||
import { IUser } from "../models/user";
|
import { IUser } from "../models/user";
|
||||||
import { IVocab } from "../models/vocab";
|
import { IVocab } from "../models/vocab";
|
||||||
|
import { IReviewCard, IReviewMetadata } from "../models/review";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
drawer: boolean;
|
drawer: boolean;
|
||||||
@ -12,6 +14,9 @@ interface IState {
|
|||||||
// TODO: Rework this
|
// TODO: Rework this
|
||||||
user: IUser | {},
|
user: IUser | {},
|
||||||
|
|
||||||
|
// All available levels
|
||||||
|
levels: ILevel[];
|
||||||
|
|
||||||
login: {
|
login: {
|
||||||
loading: boolean;
|
loading: boolean;
|
||||||
snackMsg: string;
|
snackMsg: string;
|
||||||
@ -26,7 +31,22 @@ interface IState {
|
|||||||
loading: boolean;
|
loading: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
review: {
|
||||||
|
current: IReviewCard;
|
||||||
|
|
||||||
|
loading: boolean;
|
||||||
|
vocab: IVocab[];
|
||||||
|
metadata: IReviewMetadata;
|
||||||
|
toSummary: boolean;
|
||||||
|
popoverOpen: boolean;
|
||||||
|
popoverText: string;
|
||||||
|
popoverColor: string;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
topTen: ILearner[];
|
topTen: ILearner[];
|
||||||
|
|
||||||
|
lastReview: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
const initialState: IState = {
|
const initialState: IState = {
|
||||||
@ -46,6 +66,8 @@ const initialState: IState = {
|
|||||||
snackOpen: false,
|
snackOpen: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
levels: [],
|
||||||
|
|
||||||
level: {
|
level: {
|
||||||
currentVocab: {} as IVocab,
|
currentVocab: {} as IVocab,
|
||||||
lookedAt: [0],
|
lookedAt: [0],
|
||||||
@ -54,6 +76,23 @@ const initialState: IState = {
|
|||||||
loading: true,
|
loading: true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
review: {
|
||||||
|
current: {} as IReviewCard,
|
||||||
|
|
||||||
|
loading: true,
|
||||||
|
vocab: [],
|
||||||
|
metadata: {} as IReviewMetadata,
|
||||||
|
toSummary: false,
|
||||||
|
popoverOpen: false,
|
||||||
|
popoverText: "",
|
||||||
|
popoverColor: "",
|
||||||
|
},
|
||||||
|
|
||||||
|
lastReview: {
|
||||||
|
correct: 0,
|
||||||
|
wrong: 0,
|
||||||
|
},
|
||||||
|
|
||||||
// The top ten
|
// The top ten
|
||||||
topTen: [],
|
topTen: [],
|
||||||
};
|
};
|
||||||
@ -125,7 +164,46 @@ export function LateinicusApp(state: IState = initialState, action: any) {
|
|||||||
loading: action.state,
|
loading: action.state,
|
||||||
}),
|
}),
|
||||||
});
|
});
|
||||||
|
case Actions.SET_LEVELS:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
levels: action.levels,
|
||||||
|
});
|
||||||
|
case Actions.REVIEW_SET_POPOVER:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
review: Object.assign({}, state.review, {
|
||||||
|
popoverText: action.text,
|
||||||
|
popoverOpen: action.state,
|
||||||
|
popoverColor: action.color,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.SET_REVIEW:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
review: Object.assign({}, state.review, {
|
||||||
|
current: action.current,
|
||||||
|
metadata: action.meta,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.SET_LAST_REVIEW:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
lastReview: action.metadata,
|
||||||
|
});
|
||||||
|
case Actions.REVIEW_SET_LOADING:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
review: Object.assign({}, state.review, {
|
||||||
|
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) {
|
||||||
|
console.log("Reducer not implemented:", action.type);
|
||||||
|
}
|
||||||
|
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user