diff --git a/frontend/src/actions/index.ts b/frontend/src/actions/index.ts index 5512898..ffa4999 100644 --- a/frontend/src/actions/index.ts +++ b/frontend/src/actions/index.ts @@ -249,3 +249,20 @@ export function setRegisterLoading(state: boolean) { state, }; }; + +export const VOCAB_SET_LOADING = "VOCAB_SET_LOADING"; +export function setVocabLoading(state: boolean) { + return { + type: VOCAB_SET_LOADING, + state, + }; +}; + +export const VOCAB_SET_VOCAB = "VOCAB_SET_VOCAB"; +export function setVocabVocab(vocab: IVocab[]) { + return { + type: VOCAB_SET_VOCAB, + vocab, + }; +}; + diff --git a/frontend/src/components/Drawer.tsx b/frontend/src/components/Drawer.tsx index 1d53edb..491612a 100644 --- a/frontend/src/components/Drawer.tsx +++ b/frontend/src/components/Drawer.tsx @@ -172,6 +172,16 @@ export default class Drawer extends React.Component { Vokabeln üben + + {/* + + */} + + { }); } + getVocab = (): Promise => { + return new Promise((res, rej) => { + fetch(`${BACKEND_URL}/api/user/vocab`, { + headers: new Headers({ + "Content-Type": "application/json", + "Token": this.props.user.sessionToken, + }), + }).then(resp => resp.json(), err => rej(err)) + .then(data => { + if (data.error === "200") { + res(data.data); + } else { + rej(data); + } + }); + }); + } + getLevels = (): Promise => { return new Promise((res, rej) => { fetch(`${BACKEND_URL}/api/levels`, { @@ -431,6 +450,13 @@ export default class Application extends React.Component { component={() => { return }} /> + { + return ; + }} /> ; diff --git a/frontend/src/containers/VocabPage.ts b/frontend/src/containers/VocabPage.ts new file mode 100644 index 0000000..f695161 --- /dev/null +++ b/frontend/src/containers/VocabPage.ts @@ -0,0 +1,24 @@ +import { connect } from "react-redux"; + +import { setVocabLoading, setVocabVocab } from "../actions"; + +import { IVocab } from "../models/vocab"; + +import VocabPage from "../pages/vocab"; + +const mapStateToProps = state => { + return { + loading: state.vocab.loading, + vocab: state.vocab.vocab, + }; +}; +const mapDispatchToProps = dispatch => { + return { + setLoading: (state: boolean) => dispatch(setVocabLoading(state)), + setVocab: (vocab: IVocab[]) => dispatch(setVocabVocab(vocab)), + }; +}; + +const VocabPageContainer = connect(mapStateToProps, + mapDispatchToProps)(VocabPage); +export default VocabPageContainer; diff --git a/frontend/src/pages/levelList.tsx b/frontend/src/pages/levelList.tsx index 62fbe99..aa6f33e 100644 --- a/frontend/src/pages/levelList.tsx +++ b/frontend/src/pages/levelList.tsx @@ -8,7 +8,6 @@ import CardActions from '@material-ui/core/CardActions'; import CardContent from '@material-ui/core/CardContent'; import Paper from "@material-ui/core/Paper"; import Snackbar from "@material-ui/core/Snackbar"; - import CircularProgress from "@material-ui/core/CircularProgress"; import { withRouter } from "react-router-dom"; diff --git a/frontend/src/pages/vocab.tsx b/frontend/src/pages/vocab.tsx new file mode 100644 index 0000000..d9d48fd --- /dev/null +++ b/frontend/src/pages/vocab.tsx @@ -0,0 +1,79 @@ +import * as React from "react"; + +import Grid from "@material-ui/core/Grid"; +import Typography from "@material-ui/core/Typography"; +import Paper from "@material-ui/core/Paper"; +import CircularProgress from "@material-ui/core/CircularProgress"; + +import { IVocab, VocabType } from "../models/vocab"; + +import VocabularyData from "../components/VocabularyData"; + +interface IProps { + getVocab: () => Promise; + + loading: boolean; + vocab: IVocab[]; + + setLoading: (state: boolean) => void; + setVocab: (vocab: IVocab[]) => void; +} + +export default class VocabPage extends React.Component { + private uid = 0; + genUID = () => { + return `VOCABPAGE-${this.uid++}`; + } + + componentDidMount() { + this.props.setLoading(true); + // TODO: Errorhandling + this.props.getVocab().then(vocab => { + this.props.setVocab(vocab); + this.props.setLoading(false); + }); + } + + vocabToCard = (voc: IVocab) => { + const vocabTypeToStr = { + [VocabType.NOMEN]: "Nomen", + [VocabType.VERB]: "Verb", + [VocabType.ADJEKTIV]: "Adjektiv", + [VocabType.ADVERB]: "Adverb", + }; + + return + + {`${voc.latin.grundform} (${vocabTypeToStr[voc.type]})`} + + + ; + } + + render() { + if (this.props.loading) { + return
+ + + + + + + + + +
; + } + + const { vocab } = this.props; + return
+ {vocab.map(this.vocabToCard)} +
; + } +}; diff --git a/frontend/src/reducers/index.ts b/frontend/src/reducers/index.ts index abad891..96a5c47 100644 --- a/frontend/src/reducers/index.ts +++ b/frontend/src/reducers/index.ts @@ -58,6 +58,11 @@ interface IState { showHelp: boolean; }; + vocab: { + loading: boolean; + vocab: IVocab[]; + }; + register: { loading: boolean; snackMsg: string; @@ -126,6 +131,11 @@ const initialState: IState = { showHelp: false, }, + vocab: { + loading: true, + vocab: [], + }, + register: { loading: false, snackOpen: false, @@ -313,6 +323,18 @@ export function LateinicusApp(state: IState = initialState, action: any) { leaveDialog: action.state, }), }); + case Actions.VOCAB_SET_LOADING: + return Object.assign({}, state, { + vocab: Object.assign({}, state.vocab, { + loading: action.state, + }), + }); + case Actions.VOCAB_SET_VOCAB: + return Object.assign({}, state, { + vocab: Object.assign({}, state.vocab, { + vocab: action.vocab, + }), + }); default: // Ignore the initialization call to the reducer. By that we can // catch all actions that are not implemented