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";
|
2018-09-19 16:18:32 +00:00
|
|
|
import { setSessionToken, removeSessionToken } from "../security/Token";
|
2018-08-26 14:23:48 +00:00
|
|
|
|
|
|
|
import Dashboard from "../pages/dashboard";
|
2018-09-18 16:59:15 +00:00
|
|
|
import LoginPage from "../containers/LoginPage";
|
2018-09-19 14:39:02 +00:00
|
|
|
import LevelListPage from "../containers/LevelList";
|
2018-09-18 18:20:26 +00:00
|
|
|
import LevelPage from "../containers/LevelPage";
|
2018-09-19 14:39:02 +00:00
|
|
|
import ReviewPage from "../containers/Review";
|
2018-09-19 18:30:42 +00:00
|
|
|
import SummaryPage from "../containers/SummaryPage";
|
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-09-20 18:26:40 +00:00
|
|
|
import { IResponse } from "../models/server";
|
2018-08-24 17:03:08 +00:00
|
|
|
|
2018-09-18 16:59:15 +00:00
|
|
|
interface IProps {
|
2018-09-19 16:18:32 +00:00
|
|
|
authenticated: boolean;
|
|
|
|
|
2018-09-20 19:03:46 +00:00
|
|
|
user: IUser;
|
2018-09-18 16:59:15 +00:00
|
|
|
setAuthenticated: (status: boolean) => void;
|
|
|
|
setUser: (user: IUser) => void;
|
|
|
|
};
|
|
|
|
|
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:59:15 +00:00
|
|
|
export default class Application extends React.Component<IProps> {
|
2018-09-19 16:00:38 +00:00
|
|
|
getLevels(): Promise<ILevel[]> {
|
2018-09-14 16:53:01 +00:00
|
|
|
console.log("STUB: Application::getLevels");
|
2018-08-26 14:23:48 +00:00
|
|
|
|
2018-09-19 16:00:38 +00:00
|
|
|
return new Promise((res, rej) => {
|
|
|
|
// TODO: Actually fetch them from somewhere
|
|
|
|
setTimeout(() => {
|
|
|
|
const levels = [{
|
|
|
|
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,
|
|
|
|
}];
|
|
|
|
|
|
|
|
res(levels);
|
|
|
|
|
|
|
|
}, 2000);
|
|
|
|
});
|
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-19 16:06:59 +00:00
|
|
|
getReviewQueue = (): Promise<IVocab[]> => {
|
2018-09-15 12:26:22 +00:00
|
|
|
console.log("STUB: Application::getReviewQueue");
|
|
|
|
|
|
|
|
// TODO: Implement
|
2018-09-19 16:06:59 +00:00
|
|
|
return new Promise((res, rej) => {
|
|
|
|
setTimeout(() => {
|
|
|
|
res([
|
|
|
|
{
|
|
|
|
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
|
|
|
|
} */
|
|
|
|
]);
|
|
|
|
}, 2000);
|
|
|
|
});
|
2018-09-15 12:26:22 +00:00
|
|
|
}
|
|
|
|
|
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-20 19:03:46 +00:00
|
|
|
getLevelVocab = (id: number): Promise<IVocab[]> => {
|
2018-09-18 18:20:26 +00:00
|
|
|
return new Promise((res, rej) => {
|
2018-09-20 19:03:46 +00:00
|
|
|
fetch(`${BACKEND_URL}/auth/level/${id}/vocab`, {
|
|
|
|
method: "GET",
|
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
|
|
|
}).then(data => data.json())
|
|
|
|
.then((resp: IResponse) => {
|
|
|
|
if (resp.error === "0") {
|
|
|
|
res(resp.data.vocab);
|
|
|
|
} else {
|
|
|
|
rej(resp);
|
|
|
|
}
|
|
|
|
});
|
2018-09-18 18:20:26 +00:00
|
|
|
});
|
2018-09-06 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
2018-09-20 18:26:40 +00:00
|
|
|
login = (username: string, password: string): Promise<IUser | IResponse> => {
|
2018-08-24 17:03:08 +00:00
|
|
|
return new Promise((res, rej) => {
|
2018-09-16 15:16:24 +00:00
|
|
|
fetch(`${BACKEND_URL}/login`, {
|
|
|
|
method: "POST",
|
|
|
|
headers: new Headers({
|
2018-09-18 16:59:15 +00:00
|
|
|
"Content-Type": "application/json",
|
2018-09-16 15:16:24 +00:00
|
|
|
}),
|
|
|
|
body: JSON.stringify({
|
2018-09-18 16:59:15 +00:00
|
|
|
// NOTE: We will force HTTPS, so this should not be a
|
|
|
|
// problem
|
|
|
|
username,
|
|
|
|
password,
|
2018-09-16 15:16:24 +00:00
|
|
|
}),
|
|
|
|
}).then(data => data.json())
|
2018-09-20 18:26:40 +00:00
|
|
|
.then((resp: IResponse) => {
|
2018-09-16 15:16:24 +00:00
|
|
|
if (resp.error === "0") {
|
2018-09-18 16:59:15 +00:00
|
|
|
// Successful login
|
|
|
|
this.props.setUser(resp.data);
|
2018-09-19 16:18:32 +00:00
|
|
|
setSessionToken(window, resp.data.sessionToken);
|
2018-09-20 18:26:40 +00:00
|
|
|
this.props.setAuthenticated(true);
|
2018-09-19 16:18:32 +00:00
|
|
|
|
2018-09-16 15:16:24 +00:00
|
|
|
res(resp.data);
|
|
|
|
} else {
|
2018-09-20 18:26:40 +00:00
|
|
|
rej(resp);
|
2018-09-16 15:16:24 +00:00
|
|
|
}
|
|
|
|
});
|
2018-08-24 17:03:08 +00:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2018-09-19 16:18:32 +00:00
|
|
|
logout = () => {
|
|
|
|
// TODO: Tell the server that we're logging ourselves out
|
|
|
|
removeSessionToken(window);
|
|
|
|
this.props.setAuthenticated(false);
|
|
|
|
}
|
|
|
|
|
2018-08-26 14:23:48 +00:00
|
|
|
// Checks whether the user is logged in
|
2018-09-18 16:59:15 +00:00
|
|
|
isAuthenticated = () => {
|
2018-08-26 14:23:48 +00:00
|
|
|
// TODO: Security?
|
2018-09-18 16:06:08 +00:00
|
|
|
// TODO: Implement
|
2018-09-19 16:18:32 +00:00
|
|
|
return this.props.authenticated;
|
2018-08-26 14:23:48 +00:00
|
|
|
}
|
|
|
|
|
2018-08-24 17:03:08 +00:00
|
|
|
render() {
|
2018-09-19 16:18:32 +00:00
|
|
|
// TODO: Show a spinner before mounting the routes, so that we can
|
|
|
|
// check if were authenticated before doing any requests
|
2018-08-26 14:23:48 +00:00
|
|
|
return <BrowserRouter
|
2018-08-26 17:27:21 +00:00
|
|
|
basename="/app/">
|
2018-09-19 16:18:32 +00:00
|
|
|
<div className="flex" >
|
|
|
|
<Drawer logout={this.logout} />
|
2018-08-26 14:23:48 +00:00
|
|
|
<div className="content">
|
|
|
|
<Route exact path="/" component={() => <Redirect to="/login" />} />
|
|
|
|
<Route exact path="/login" component={() => {
|
2018-09-18 16:59:15 +00:00
|
|
|
return <LoginPage login={this.login} />
|
2018-08-26 14:23:48 +00:00
|
|
|
}} />
|
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-19 16:00:38 +00:00
|
|
|
component={() => <LevelListPage
|
|
|
|
getLevels={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
|
|
|
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-19 18:30:42 +00:00
|
|
|
return <SummaryPage />
|
2018-09-12 17:23:00 +00:00
|
|
|
}} />
|
2018-08-26 14:23:48 +00:00
|
|
|
</div>
|
2018-09-18 16:59:15 +00:00
|
|
|
</div >
|
|
|
|
</BrowserRouter >;
|
2018-08-24 17:03:08 +00:00
|
|
|
}
|
|
|
|
};
|