refactor: Move the LevelPage to Redux
This commit is contained in:
parent
95fc93f690
commit
3136c92687
@ -1,3 +1,6 @@
|
|||||||
|
import { IVocab } from "../models/vocab";
|
||||||
|
import { IUser } from "../models/user";
|
||||||
|
|
||||||
export const SET_DRAWER = "SET_DRAWER";
|
export const SET_DRAWER = "SET_DRAWER";
|
||||||
export function setDrawer(state: boolean) {
|
export function setDrawer(state: boolean) {
|
||||||
return {
|
return {
|
||||||
@ -46,3 +49,43 @@ export function setUser(user: IUser) {
|
|||||||
user,
|
user,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const LEVEL_SET_REVIEW = "LEVEL_SET_REVIEW";
|
||||||
|
export function setLevelReview(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: LEVEL_SET_REVIEW,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LEVEL_SET_LOOKEDAT = "LEVEL_SET_LOOKEDAT";
|
||||||
|
export function setLevelLookedAt(ids: number[]) {
|
||||||
|
return {
|
||||||
|
type: LEVEL_SET_REVIEW,
|
||||||
|
lookedAt: ids,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LEVEL_SET_CUR_VOCAB = "LEVEL_SET_CUR_VOCAB";
|
||||||
|
export function setLevelCurrentVocab(vocab: IVocab) {
|
||||||
|
return {
|
||||||
|
type: LEVEL_SET_CUR_VOCAB,
|
||||||
|
vocab,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LEVEL_SET_VOCAB = "LEVEL_SET_VOCAB";
|
||||||
|
export function setLevelVocab(vocab: IVocab[]) {
|
||||||
|
return {
|
||||||
|
type: LEVEL_SET_VOCAB,
|
||||||
|
vocab,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
export const LEVEL_SET_LOADING = "LEVEL_SET_LOADING";
|
||||||
|
export function setLevelLoading(state: boolean) {
|
||||||
|
return {
|
||||||
|
type: LEVEL_SET_LOADING,
|
||||||
|
state,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
@ -7,7 +7,7 @@ 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 "../pages/levelList";
|
||||||
import LevelPage from "../pages/level";
|
import LevelPage from "../containers/LevelPage";
|
||||||
import ReviewPage from "../pages/review";
|
import ReviewPage from "../pages/review";
|
||||||
import SummaryPage from "../pages/summary";
|
import SummaryPage from "../pages/summary";
|
||||||
import WelcomePage from "../pages/intro";
|
import WelcomePage from "../pages/intro";
|
||||||
@ -146,12 +146,13 @@ export default class Application extends React.Component<IProps> {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
getLevelVocab(id: number): IVocab[] {
|
getLevelVocab(id: number): Promise<IVocab[]> {
|
||||||
console.log("STUB: Application::getLevelVocab");
|
console.log("STUB: Application::getLevelVocab");
|
||||||
|
|
||||||
// TODO: Actually implement this
|
// TODO: Actually implement this
|
||||||
// TODO: Don't fetch this when it was already fetched once.
|
return new Promise((res, rej) => {
|
||||||
return [{
|
setTimeout(() => {
|
||||||
|
res([{
|
||||||
german: ["Wein"],
|
german: ["Wein"],
|
||||||
hint: "Worte auf '-um' sind meistens NeutrUM",
|
hint: "Worte auf '-um' sind meistens NeutrUM",
|
||||||
type: VocabType.NOMEN,
|
type: VocabType.NOMEN,
|
||||||
@ -174,7 +175,9 @@ export default class Application extends React.Component<IProps> {
|
|||||||
* hint: "Worte auf \"-a\" sind FeminA",
|
* hint: "Worte auf \"-a\" sind FeminA",
|
||||||
* type: VocabType.NOMEN,
|
* type: VocabType.NOMEN,
|
||||||
* id: 3
|
* id: 3
|
||||||
} */] as IVocab[];
|
} */]);
|
||||||
|
}, 2000);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
login = (username: string, password: string): Promise<IUser | {}> => {
|
login = (username: string, password: string): Promise<IUser | {}> => {
|
||||||
|
37
src/containers/LevelPage.ts
Normal file
37
src/containers/LevelPage.ts
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
import { connect } from "react-redux";
|
||||||
|
|
||||||
|
import {
|
||||||
|
setDrawerButton, setLevelReview, setLevelLookedAt,
|
||||||
|
setLevelCurrentVocab, setLevelVocab, setLevelLoading
|
||||||
|
} from "../actions";
|
||||||
|
|
||||||
|
import { IVocab } from "../models/vocab";
|
||||||
|
|
||||||
|
import LevelPage from "../pages/level";
|
||||||
|
|
||||||
|
const mapStateToProps = state => {
|
||||||
|
const { currentVocab, lookedAt, toReview, vocab, loading } = state.level;
|
||||||
|
|
||||||
|
return {
|
||||||
|
currentVocab,
|
||||||
|
lookedAt,
|
||||||
|
toReview,
|
||||||
|
vocab,
|
||||||
|
loading,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const mapDispatchToProps = dispatch => {
|
||||||
|
return {
|
||||||
|
drawerButtonState: (state: boolean) => dispatch(setDrawerButton(state)),
|
||||||
|
setReview: (state: boolean) => dispatch(setLevelReview(state)),
|
||||||
|
setLookedAt: (ids: number[]) => dispatch(setLevelLookedAt(ids)),
|
||||||
|
setCurrentVocab: (vocab: IVocab) => dispatch(setLevelCurrentVocab(vocab)),
|
||||||
|
setVocab: (vocab: IVocab[]) => dispatch(setLevelVocab(vocab)),
|
||||||
|
setLoading: (state: boolean) => dispatch(setLevelLoading(state)),
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
const LevelPageContainer = connect(mapStateToProps,
|
||||||
|
mapDispatchToProps)(LevelPage);
|
||||||
|
export default LevelPageContainer;
|
@ -5,8 +5,11 @@ import ListItem from "@material-ui/core/ListItem";
|
|||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
import Grid from "@material-ui/core/Grid";
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography";
|
||||||
|
import Paper from "@material-ui/core/Paper";
|
||||||
|
import Grid from "@material-ui/core/Grid";
|
||||||
import Card from "@material-ui/core/Card";
|
import Card from "@material-ui/core/Card";
|
||||||
import CardContent from "@material-ui/core/CardContent";
|
import CardContent from "@material-ui/core/CardContent";
|
||||||
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
|
||||||
import { Redirect } from "react-router-dom";
|
import { Redirect } from "react-router-dom";
|
||||||
|
|
||||||
@ -14,35 +17,42 @@ import { IVocab } from "../models/vocab";
|
|||||||
|
|
||||||
interface IProps {
|
interface IProps {
|
||||||
id: string;
|
id: string;
|
||||||
|
levelVocab: (id: string) => Promise<IVocab[]>;
|
||||||
|
|
||||||
levelVocab: (id: string) => IVocab[];
|
loading: boolean;
|
||||||
|
setLoading: (state: boolean) => void;
|
||||||
|
vocab: IVocab[];
|
||||||
|
setVocab: (vocab: IVocab[]) => void;
|
||||||
|
setLookedAt: (ids: number[]) => void;
|
||||||
|
setCurrentVocab: (vocab: IVocab) => void;
|
||||||
|
setReview: (state: boolean) => void;
|
||||||
drawerButtonState: (state: boolean) => void;
|
drawerButtonState: (state: boolean) => void;
|
||||||
};
|
|
||||||
|
|
||||||
interface IState {
|
|
||||||
currentVocab: IVocab;
|
currentVocab: IVocab;
|
||||||
lookedAt: number[];
|
lookedAt: number[];
|
||||||
toReview: boolean;
|
toReview: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default class LevelPage extends React.Component<IProps, IState> {
|
export default class LevelPage extends React.Component<IProps> {
|
||||||
private uid = 0;
|
private uid = 0;
|
||||||
// To prevent React from redrawing the vocabulary list and prematurely
|
// To prevent React from redrawing the vocabulary list and prematurely
|
||||||
// cancelling the animation
|
// cancelling the animation
|
||||||
private uids: { [key: string]: string } = {};
|
private uids: { [key: string]: string } = {};
|
||||||
|
|
||||||
constructor(props: any) {
|
componentDidMount() {
|
||||||
super(props);
|
|
||||||
|
|
||||||
// Hide the drawer
|
// Hide the drawer
|
||||||
this.props.drawerButtonState(false);
|
this.props.drawerButtonState(false);
|
||||||
|
|
||||||
this.state = {
|
// Fetch the vocabulary
|
||||||
currentVocab: this.props.levelVocab(this.props.id)[0],
|
this.props.setLoading(true);
|
||||||
lookedAt: [0],
|
|
||||||
toReview: false,
|
// TODO: Error handling
|
||||||
};
|
this.props.levelVocab(this.props.id).then(vocab => {
|
||||||
|
this.props.setVocab(vocab);
|
||||||
|
this.props.setCurrentVocab(vocab[0]);
|
||||||
|
this.props.setLoading(false);
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
genUID = (vocab: IVocab): string => {
|
genUID = (vocab: IVocab): string => {
|
||||||
@ -57,16 +67,16 @@ export default class LevelPage extends React.Component<IProps, IState> {
|
|||||||
|
|
||||||
renderVocabListItem = (vocab: IVocab): any => {
|
renderVocabListItem = (vocab: IVocab): any => {
|
||||||
// Check if the vocab was already looked at
|
// Check if the vocab was already looked at
|
||||||
const lookedAt = this.state.lookedAt.find((el) => el === vocab.id) || vocab.id === 0;
|
const lookedAt = this.props.lookedAt.find((el) => el === vocab.id) || vocab.id === 0;
|
||||||
|
|
||||||
return <ListItem button key={this.genUID(vocab)} onClick={() => {
|
return <ListItem button key={this.genUID(vocab)} onClick={() => {
|
||||||
// Prevent the user from using too much memory by always clicking on the elements
|
// Prevent the user from using too much memory by always clicking on the elements
|
||||||
// Show the clicked at vocab word
|
// Show the clicked at vocab word
|
||||||
|
|
||||||
this.setState({
|
this.props.setCurrentVocab(vocab);
|
||||||
currentVocab: vocab,
|
this.props.setLookedAt(lookedAt ? (
|
||||||
lookedAt: lookedAt ? this.state.lookedAt : this.state.lookedAt.concat(vocab.id),
|
this.props.lookedAt
|
||||||
});
|
) : this.props.lookedAt.concat(vocab.id));
|
||||||
}}>
|
}}>
|
||||||
<ListItemText>
|
<ListItemText>
|
||||||
{`${vocab.latin.grundform} ${lookedAt ? "✔" : ""}`}
|
{`${vocab.latin.grundform} ${lookedAt ? "✔" : ""}`}
|
||||||
@ -75,23 +85,52 @@ export default class LevelPage extends React.Component<IProps, IState> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
toReview = () => {
|
toReview = () => {
|
||||||
const { levelVocab, id } = this.props;
|
const { vocab, lookedAt } = this.props;
|
||||||
// Only go to the review if all vocabulary item have been looked at
|
// Only go to the review if all vocabulary item have been looked at
|
||||||
if (levelVocab(id).length === this.state.lookedAt.length) {
|
if (vocab.length === lookedAt.length) {
|
||||||
this.setState({
|
this.props.setReview(true);
|
||||||
toReview: true,
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { currentVocab } = this.state;
|
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.toReview ? (
|
||||||
|
<Redirect to={`/review/level/${this.props.id}`} />
|
||||||
|
) : 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 { currentVocab } = this.props;
|
||||||
return <div>
|
return <div>
|
||||||
<Grid container direction="row">
|
<Grid container direction="row">
|
||||||
<Grid item xs={3}>
|
<Grid item xs={3}>
|
||||||
<List>
|
<List>
|
||||||
{this.props.levelVocab(this.props.id)
|
{this.props.vocab
|
||||||
.map(this.renderVocabListItem)}
|
.map(this.renderVocabListItem)}
|
||||||
{/* TODO*/}
|
{/* TODO*/}
|
||||||
<ListItem button onClick={this.toReview}>
|
<ListItem button onClick={this.toReview}>
|
||||||
@ -102,7 +141,7 @@ export default class LevelPage extends React.Component<IProps, IState> {
|
|||||||
</List>
|
</List>
|
||||||
</Grid>
|
</Grid>
|
||||||
{
|
{
|
||||||
this.state.toReview ? (
|
this.props.toReview ? (
|
||||||
<Redirect to={`/review/level/${this.props.id}`} />
|
<Redirect to={`/review/level/${this.props.id}`} />
|
||||||
) : undefined
|
) : undefined
|
||||||
}
|
}
|
||||||
|
@ -2,6 +2,7 @@ import * as Actions from "../actions";
|
|||||||
|
|
||||||
import { ILearner } from "../models/learner";
|
import { ILearner } from "../models/learner";
|
||||||
import { IUser } from "../models/user";
|
import { IUser } from "../models/user";
|
||||||
|
import { IVocab } from "../models/vocab";
|
||||||
|
|
||||||
interface IState {
|
interface IState {
|
||||||
drawer: boolean;
|
drawer: boolean;
|
||||||
@ -17,6 +18,14 @@ interface IState {
|
|||||||
snackOpen: boolean;
|
snackOpen: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
level: {
|
||||||
|
currentVocab: IVocab;
|
||||||
|
lookedAt: number[];
|
||||||
|
toReview: boolean;
|
||||||
|
vocab: IVocab[];
|
||||||
|
loading: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
topTen: ILearner[];
|
topTen: ILearner[];
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,7 +36,7 @@ const initialState: IState = {
|
|||||||
drawerButton: true,
|
drawerButton: true,
|
||||||
// Is the user authenticated?
|
// Is the user authenticated?
|
||||||
// TODO: Set this to false
|
// TODO: Set this to false
|
||||||
authenticated: false,
|
authenticated: true,
|
||||||
|
|
||||||
user: {},
|
user: {},
|
||||||
|
|
||||||
@ -37,6 +46,14 @@ const initialState: IState = {
|
|||||||
snackOpen: false,
|
snackOpen: false,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
level: {
|
||||||
|
currentVocab: {} as IVocab,
|
||||||
|
lookedAt: [0],
|
||||||
|
toReview: false,
|
||||||
|
vocab: [],
|
||||||
|
loading: true,
|
||||||
|
},
|
||||||
|
|
||||||
// The top ten
|
// The top ten
|
||||||
topTen: [],
|
topTen: [],
|
||||||
};
|
};
|
||||||
@ -75,6 +92,39 @@ export function LateinicusApp(state: IState = initialState, action: any) {
|
|||||||
return Object.assign({}, state, {
|
return Object.assign({}, state, {
|
||||||
user: action.user,
|
user: action.user,
|
||||||
});
|
});
|
||||||
|
case Actions.LEVEL_SET_REVIEW:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
level: Object.assign({}, state.level, {
|
||||||
|
toReview: action.state,
|
||||||
|
// Make sure that we are in a "loading mode", when the page gets
|
||||||
|
// called the next time
|
||||||
|
loading: true,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.LEVEL_SET_LOOKEDAT:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
level: Object.assign({}, state.level, {
|
||||||
|
lookedAt: action.lookedAt,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.LEVEL_SET_CUR_VOCAB:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
level: Object.assign({}, state.level, {
|
||||||
|
currentVocab: action.vocab,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.LEVEL_SET_VOCAB:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
level: Object.assign({}, state.level, {
|
||||||
|
vocab: action.vocab,
|
||||||
|
}),
|
||||||
|
});
|
||||||
|
case Actions.LEVEL_SET_LOADING:
|
||||||
|
return Object.assign({}, state, {
|
||||||
|
level: Object.assign({}, state.level, {
|
||||||
|
loading: action.state,
|
||||||
|
}),
|
||||||
|
});
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user