2018-08-24 17:03:08 +00:00
|
|
|
import * as React from "react";
|
|
|
|
|
2018-09-18 16:06:08 +00:00
|
|
|
import { BrowserRouter, Route, Redirect } from "react-router-dom";
|
2018-08-26 14:23:48 +00:00
|
|
|
|
|
|
|
import AuthRoute from "../security/AuthRoute";
|
|
|
|
|
|
|
|
import Dashboard from "../pages/dashboard";
|
2018-08-24 17:03:08 +00:00
|
|
|
import LoginPage from "../pages/login";
|
2018-09-06 18:13:29 +00:00
|
|
|
import LevelListPage from "../pages/levelList";
|
2018-09-06 18:05:21 +00:00
|
|
|
import LevelPage from "../pages/level";
|
2018-09-12 13:15:55 +00:00
|
|
|
import ReviewPage from "../pages/review";
|
2018-09-12 17:23:00 +00:00
|
|
|
import SummaryPage from "../pages/summary";
|
2018-09-15 13:28:27 +00:00
|
|
|
import WelcomePage from "../pages/intro";
|
2018-08-26 14:23:48 +00:00
|
|
|
|
2018-09-18 16:06:08 +00:00
|
|
|
import Drawer from "../containers/Drawer";
|
|
|
|
|
2018-09-16 15:16:24 +00:00
|
|
|
import { BACKEND_URL } from "../config";
|
|
|
|
|
2018-09-06 18:13:29 +00:00
|
|
|
import { ILevel } from "../models/level";
|
2018-09-14 16:53:01 +00:00
|
|
|
import { ILearner } from "../models/learner";
|
2018-09-12 17:23:00 +00:00
|
|
|
import { IVocab, VocabType } from "../models/vocab";
|
2018-09-15 12:26:22 +00:00
|
|
|
import { IReviewMetadata, ReviewType } from "../models/review";
|
2018-09-16 15:16:24 +00:00
|
|
|
import { IUser } from "../models/user";
|
2018-08-24 17:03:08 +00:00
|
|
|
|
2018-09-14 16:53:01 +00:00
|
|
|
// TODO: Replace the sessionStorage with localStorage?
|
|
|
|
// TODO: Cache API-Calls
|
2018-09-16 15:16:24 +00:00
|
|
|
// TODO: When mounting without a login, check if the sessionToken is still valid
|
2018-09-18 16:06:08 +00:00
|
|
|
export default class Application extends React.Component<{}> {
|
2018-08-24 17:03:08 +00:00
|
|
|
constructor(props: any) {
|
|
|
|
super(props);
|
|
|
|
|
2018-09-06 18:22:41 +00:00
|
|
|
// Load a key from the SessionStorage
|
2018-09-16 15:16:24 +00:00
|
|
|
const authKey = window.sessionStorage.getItem("sessionToken") || null;
|
2018-09-06 18:22:41 +00:00
|
|
|
// TODO
|
|
|
|
const loggedIn = authKey !== null;
|
|
|
|
|
2018-08-24 17:03:08 +00:00
|
|
|
this.login = this.login.bind(this);
|
2018-08-26 14:23:48 +00:00
|
|
|
this.isAuthenticated = this.isAuthenticated.bind(this);
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:13:29 +00:00
|
|
|
getLevels(): ILevel[] {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getLevels");
|
2018-08-26 15:12:07 +00:00
|
|
|
// TODO: Actually fetch them from somewhere
|
2018-09-06 18:13:29 +00:00
|
|
|
const levels = [{
|
2018-08-26 14:23:48 +00:00
|
|
|
name: "Der Bauer auf dem Feld",
|
|
|
|
desc: "So fängt alles an: Du bist ein einfacher Bauer und musst dich die Karriereleiter mit deinen freshen Latein-Skills hinaufarbeiten",
|
|
|
|
level: 1,
|
|
|
|
done: true,
|
|
|
|
}, {
|
|
|
|
name: "???",
|
|
|
|
desc: "Warum schreibe ich überhaupt was?dsd dddddddddddddddddddddd",
|
|
|
|
level: 2,
|
|
|
|
done: false,
|
|
|
|
}];
|
|
|
|
|
2018-09-06 18:13:29 +00:00
|
|
|
return levels;
|
2018-08-24 17:03:08 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 15:32:20 +00:00
|
|
|
getLastReview = (): IReviewMetadata => {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getLastReview");
|
|
|
|
|
2018-09-12 17:23:00 +00:00
|
|
|
// TODO: Actually fetch this
|
2018-09-18 16:06:08 +00:00
|
|
|
// TODO: Stub
|
|
|
|
return {} as IReviewMetadata;
|
2018-09-15 15:32:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
setLastReview = (meta: IReviewMetadata) => {
|
2018-09-16 15:16:24 +00:00
|
|
|
console.log("STUB: Application::setLastReview");
|
|
|
|
|
2018-09-15 15:32:20 +00:00
|
|
|
// TODO: Send this to the server
|
|
|
|
this.setState({
|
|
|
|
lastReview: meta,
|
|
|
|
});
|
2018-09-12 17:23:00 +00:00
|
|
|
}
|
|
|
|
|
2018-09-15 12:26:22 +00:00
|
|
|
getReviewQueue = (): IVocab[] => {
|
|
|
|
console.log("STUB: Application::getReviewQueue");
|
|
|
|
|
|
|
|
// TODO: Implement
|
|
|
|
return [{
|
|
|
|
german: ["Wein"],
|
|
|
|
hint: "Worte auf '-um' sind meistens NeutrUM",
|
|
|
|
type: VocabType.NOMEN,
|
|
|
|
latin: {
|
|
|
|
grundform: "Vinum",
|
|
|
|
genitiv: "Vini",
|
|
|
|
genus: "Neutrum"
|
|
|
|
},
|
|
|
|
id: 0
|
|
|
|
}/* , {
|
|
|
|
* latin: "Vici",
|
|
|
|
* german: "<Wortbedeutung>",
|
|
|
|
* hint: "Wird \"Viki\" und nicht \"Vichi\" ausgesprochen",
|
|
|
|
* mnemonic: "Merk dir das Wort mit Caesars berühmten Worten: \"Veni Vidi Vici\"; Er kam, sah und siegte",
|
|
|
|
* type: VocabType.NOMEN,
|
|
|
|
* id: 2
|
|
|
|
}, {
|
|
|
|
* latin: "fuga",
|
|
|
|
* german: "Flucht",
|
|
|
|
* hint: "Worte auf \"-a\" sind FeminA",
|
|
|
|
* type: VocabType.NOMEN,
|
|
|
|
* id: 3
|
|
|
|
} */] as IVocab[];
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:05:21 +00:00
|
|
|
getLearners(): ILearner[] {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getLearners");
|
|
|
|
|
|
|
|
// TODO: Implement
|
|
|
|
return [{
|
|
|
|
username: "Polynomdivision",
|
|
|
|
level: 5,
|
|
|
|
score: 400,
|
|
|
|
}, {
|
|
|
|
username: "Polynomdivision2",
|
|
|
|
level: 3,
|
|
|
|
score: 500,
|
|
|
|
}, {
|
|
|
|
username: "Der eine Typ",
|
|
|
|
level: 7,
|
|
|
|
score: 100,
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
|
|
|
getTopTenLearners(): ILearner[] {
|
|
|
|
console.log("STUB: Application::getTopTenLearners");
|
|
|
|
|
|
|
|
// TODO: Implement
|
2018-08-26 15:12:07 +00:00
|
|
|
return [{
|
|
|
|
username: "Polynomdivision",
|
|
|
|
level: 5,
|
|
|
|
score: 400,
|
|
|
|
}, {
|
|
|
|
username: "Polynomdivision2",
|
|
|
|
level: 3,
|
|
|
|
score: 500,
|
|
|
|
}, {
|
|
|
|
username: "Der eine Typ",
|
|
|
|
level: 7,
|
|
|
|
score: 100,
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2018-09-06 18:13:29 +00:00
|
|
|
getNextLevel(): ILevel {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getNextLevel");
|
|
|
|
|
2018-09-06 18:05:21 +00:00
|
|
|
// TODO: Actually fetch data
|
|
|
|
return {
|
|
|
|
name: "???",
|
|
|
|
desc: "Warum schreibe ich überhaupt was?dsd dddddddddddddddddddddd",
|
|
|
|
level: 2,
|
|
|
|
done: false,
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2018-09-15 12:26:22 +00:00
|
|
|
getLevelVocab(id: number): IVocab[] {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getLevelVocab");
|
|
|
|
|
2018-09-06 18:05:21 +00:00
|
|
|
// TODO: Actually implement this
|
|
|
|
// TODO: Don't fetch this when it was already fetched once.
|
|
|
|
return [{
|
2018-09-14 14:13:42 +00:00
|
|
|
german: ["Wein"],
|
2018-09-14 15:18:55 +00:00
|
|
|
hint: "Worte auf '-um' sind meistens NeutrUM",
|
2018-09-12 17:23:00 +00:00
|
|
|
type: VocabType.NOMEN,
|
2018-09-14 14:13:42 +00:00
|
|
|
latin: {
|
|
|
|
grundform: "Vinum",
|
|
|
|
genitiv: "Vini",
|
|
|
|
genus: "Neutrum"
|
|
|
|
},
|
2018-09-06 18:05:21 +00:00
|
|
|
id: 0
|
2018-09-12 17:23:00 +00:00
|
|
|
}/* , {
|
|
|
|
* latin: "Vici",
|
|
|
|
* german: "<Wortbedeutung>",
|
|
|
|
* hint: "Wird \"Viki\" und nicht \"Vichi\" ausgesprochen",
|
|
|
|
* mnemonic: "Merk dir das Wort mit Caesars berühmten Worten: \"Veni Vidi Vici\"; Er kam, sah und siegte",
|
|
|
|
* type: VocabType.NOMEN,
|
|
|
|
* id: 2
|
|
|
|
}, {
|
|
|
|
* latin: "fuga",
|
|
|
|
* german: "Flucht",
|
|
|
|
* hint: "Worte auf \"-a\" sind FeminA",
|
|
|
|
* type: VocabType.NOMEN,
|
|
|
|
* id: 3
|
|
|
|
} */] as IVocab[];
|
2018-09-06 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
2018-09-16 15:16:24 +00:00
|
|
|
login(username: string, password: string): Promise<IUser | {}> {
|
2018-08-24 17:03:08 +00:00
|
|
|
return new Promise((res, rej) => {
|
2018-09-15 13:28:27 +00:00
|
|
|
// TODO: First login? Redirect to /welcome
|
2018-09-16 15:16:24 +00:00
|
|
|
fetch(`${BACKEND_URL}/login`, {
|
|
|
|
method: "POST",
|
|
|
|
headers: new Headers({
|
|
|
|
'Content-Type': "application/json",
|
|
|
|
}),
|
|
|
|
body: JSON.stringify({
|
|
|
|
// NOTE: We will force HTTPS and hash using pbkdf2 on the
|
|
|
|
// server
|
|
|
|
username: username,
|
|
|
|
hash: password,
|
|
|
|
}),
|
|
|
|
}).then(data => data.json())
|
|
|
|
.then((resp) => {
|
|
|
|
if (resp.error === "0") {
|
|
|
|
this.setState({
|
|
|
|
loggedIn: true,
|
|
|
|
user: resp.data,
|
|
|
|
});
|
|
|
|
res(resp.data);
|
|
|
|
} else {
|
|
|
|
rej({});
|
|
|
|
}
|
|
|
|
});
|
2018-08-24 17:03:08 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-08-26 14:23:48 +00:00
|
|
|
// Checks whether the user is logged in
|
|
|
|
isAuthenticated() {
|
|
|
|
// TODO: Security?
|
2018-09-18 16:06:08 +00:00
|
|
|
// TODO: Implement
|
|
|
|
return true;
|
2018-08-26 14:23:48 +00:00
|
|
|
}
|
|
|
|
|
2018-09-14 15:18:55 +00:00
|
|
|
closeDrawer = () => {
|
|
|
|
this.setState({
|
|
|
|
drawerOpen: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-09-15 12:26:22 +00:00
|
|
|
drawerButtonState = (show: boolean) => {
|
|
|
|
// This is required as we would otherwise try to continously update
|
|
|
|
// the state, resulting in an infinte loop
|
|
|
|
if (show !== this.state.showDrawerButton) {
|
|
|
|
this.setState({
|
|
|
|
showDrawerButton: show,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-24 17:03:08 +00:00
|
|
|
render() {
|
2018-08-26 14:23:48 +00:00
|
|
|
return <BrowserRouter
|
2018-08-26 17:27:21 +00:00
|
|
|
basename="/app/">
|
2018-08-26 14:23:48 +00:00
|
|
|
<div className="flex">
|
2018-09-18 16:06:08 +00:00
|
|
|
<Drawer />
|
2018-08-26 14:23:48 +00:00
|
|
|
<div className="content">
|
|
|
|
<Route exact path="/" component={() => <Redirect to="/login" />} />
|
|
|
|
<Route exact path="/login" component={() => {
|
|
|
|
return <LoginPage loggedIn={this.state.loggedIn} login={this.login} />
|
|
|
|
}} />
|
2018-08-26 15:12:07 +00:00
|
|
|
<AuthRoute
|
|
|
|
isAuth={this.isAuthenticated}
|
|
|
|
path="/dashboard"
|
2018-09-12 17:23:00 +00:00
|
|
|
component={() => {
|
|
|
|
return <Dashboard
|
|
|
|
nextLevel={this.getNextLevel}
|
2018-09-15 15:32:20 +00:00
|
|
|
getLastReview={this.getLastReview}
|
2018-09-14 16:53:01 +00:00
|
|
|
getTopTen={this.getTopTenLearners} />
|
2018-09-12 17:23:00 +00:00
|
|
|
}} />
|
2018-09-15 13:28:27 +00:00
|
|
|
<AuthRoute
|
|
|
|
isAuth={this.isAuthenticated}
|
|
|
|
path="/welcome"
|
|
|
|
component={() => {
|
|
|
|
return <WelcomePage />
|
|
|
|
}} />
|
2018-08-26 15:12:07 +00:00
|
|
|
<AuthRoute
|
|
|
|
isAuth={this.isAuthenticated}
|
2018-09-06 18:05:21 +00:00
|
|
|
path="/levelList"
|
2018-09-15 12:26:22 +00:00
|
|
|
component={() => <LevelListPage
|
|
|
|
levels={this.getLevels()} />} />
|
2018-09-06 18:05:21 +00:00
|
|
|
{/*We cannot use AuthRoute here, because match is undefined otherwise*/}
|
|
|
|
<Route
|
|
|
|
path="/level/:id"
|
|
|
|
component={({ match }) => {
|
|
|
|
if (this.isAuthenticated()) {
|
2018-09-15 12:26:22 +00:00
|
|
|
return <LevelPage
|
|
|
|
id={match.params.id}
|
|
|
|
levelVocab={this.getLevelVocab}
|
2018-09-15 15:32:20 +00:00
|
|
|
drawerButtonState={this.drawerButtonState}
|
|
|
|
setLastReview={this.setLastReview} />;
|
2018-09-06 18:05:21 +00:00
|
|
|
} else {
|
|
|
|
return <Redirect to="/login" />;
|
|
|
|
}
|
|
|
|
}} />
|
2018-09-12 13:15:55 +00:00
|
|
|
<Route
|
|
|
|
path="/review/level/:id"
|
|
|
|
component={({ match }) => {
|
|
|
|
if (this.isAuthenticated()) {
|
2018-09-15 12:26:22 +00:00
|
|
|
return <ReviewPage
|
|
|
|
reviewType={ReviewType.LEVEL}
|
|
|
|
levelId={match.params.id}
|
|
|
|
vocabByLevel={this.getLevelVocab}
|
2018-09-15 15:32:20 +00:00
|
|
|
drawerButtonState={this.drawerButtonState}
|
|
|
|
setLastReview={this.setLastReview} />;
|
2018-09-12 13:15:55 +00:00
|
|
|
} else {
|
|
|
|
return <Redirect to="/login" />;
|
|
|
|
}
|
|
|
|
}} />
|
2018-09-14 15:18:55 +00:00
|
|
|
<AuthRoute
|
|
|
|
isAuth={this.isAuthenticated}
|
|
|
|
path="/review/queue"
|
|
|
|
component={() => {
|
2018-09-15 12:26:22 +00:00
|
|
|
return <ReviewPage
|
|
|
|
reviewType={ReviewType.QUEUE}
|
|
|
|
vocabByQueue={this.getReviewQueue}
|
2018-09-15 15:32:20 +00:00
|
|
|
drawerButtonState={this.drawerButtonState}
|
|
|
|
setLastReview={this.setLastReview} />;
|
2018-09-14 15:18:55 +00:00
|
|
|
}} />
|
2018-09-12 17:23:00 +00:00
|
|
|
<AuthRoute
|
|
|
|
isAuth={this.isAuthenticated}
|
|
|
|
path="/review/summary"
|
|
|
|
component={() => {
|
2018-09-15 12:26:22 +00:00
|
|
|
return <SummaryPage
|
|
|
|
reviewMeta={this.getLastReview} />
|
2018-09-12 17:23:00 +00:00
|
|
|
}} />
|
2018-08-26 14:23:48 +00:00
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
</BrowserRouter>;
|
2018-08-24 17:03:08 +00:00
|
|
|
}
|
|
|
|
};
|