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-23 14:30:14 +00:00
|
|
|
import { setSessionToken, removeSessionToken, getSessionToken } from "../security/Token";
|
2018-08-26 14:23:48 +00:00
|
|
|
|
2018-09-23 14:14:14 +00:00
|
|
|
import Dashboard from "../containers/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-23 14:14:14 +00:00
|
|
|
import { ILearner, TopTen } 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;
|
2018-09-23 14:30:14 +00:00
|
|
|
setDidLogin: (status: boolean) => void;
|
2018-09-18 16:59:15 +00:00
|
|
|
setUser: (user: IUser) => void;
|
|
|
|
};
|
|
|
|
|
2018-09-14 16:53:01 +00:00
|
|
|
// TODO: Replace the sessionStorage with localStorage?
|
2018-09-18 16:59:15 +00:00
|
|
|
export default class Application extends React.Component<IProps> {
|
2018-09-23 14:30:14 +00:00
|
|
|
componentDidMount() {
|
|
|
|
// TODO: Ask the server if our session is still valid
|
|
|
|
// TODO: When asking the server if our session is still valid, a spinner
|
|
|
|
// should be shown
|
|
|
|
if (getSessionToken(window) !== null) {
|
2018-09-23 20:22:22 +00:00
|
|
|
// TODO: We still need to fetch the user data
|
2018-09-23 14:30:14 +00:00
|
|
|
this.props.setAuthenticated(true);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-09-24 11:36:42 +00:00
|
|
|
getLevels = (): Promise<ILevel[]> => {
|
2018-09-19 16:00:38 +00:00
|
|
|
return new Promise((res, rej) => {
|
2018-09-24 11:36:42 +00:00
|
|
|
fetch(`${BACKEND_URL}/api/levels`, {
|
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
|
|
|
}).then(resp => resp.json(), err => rej(err))
|
|
|
|
.then(data => {
|
|
|
|
if (data.error === "0") {
|
|
|
|
res(data.data.levels);
|
|
|
|
} else {
|
|
|
|
rej(data);
|
|
|
|
}
|
|
|
|
});
|
2018-09-19 16:00:38 +00:00
|
|
|
});
|
2018-08-24 17:03:08 +00:00
|
|
|
}
|
|
|
|
|
2018-09-24 11:36:42 +00:00
|
|
|
getLastReview = (): Promise<IReviewMetadata> => {
|
|
|
|
return new Promise((res, rej) => {
|
|
|
|
fetch(`${BACKEND_URL}/api/user/lastReview`, {
|
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
|
|
|
}).then(resp => resp.json(), err => rej(err))
|
|
|
|
.then(data => {
|
|
|
|
if (data.error === "0") {
|
|
|
|
res(data.data);
|
|
|
|
} else {
|
|
|
|
rej(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
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
|
2018-09-24 11:36:42 +00:00
|
|
|
/* 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,
|
|
|
|
}];
|
|
|
|
}
|
|
|
|
|
2018-09-23 14:14:14 +00:00
|
|
|
getTopTenLearners = (): Promise<TopTen[]> => {
|
|
|
|
const id = this.props.user.classId;
|
|
|
|
return new Promise((res, rej) => {
|
2018-09-23 20:22:22 +00:00
|
|
|
fetch(`${BACKEND_URL}/api/class/${id}/topTen`, {
|
2018-09-23 14:14:14 +00:00
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
|
|
|
}).then(resp => resp.json(),
|
|
|
|
err => rej(err))
|
|
|
|
.then(data => {
|
|
|
|
if (data.error === "0") {
|
|
|
|
res(data.data.topTen);
|
|
|
|
} else {
|
|
|
|
rej(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2018-08-26 15:12:07 +00:00
|
|
|
}
|
|
|
|
|
2018-09-23 14:14:14 +00:00
|
|
|
getNextLevel = (): Promise<ILevel> => {
|
|
|
|
return new Promise((res, rej) => {
|
2018-09-23 20:22:22 +00:00
|
|
|
fetch(`${BACKEND_URL}/api/user/nextLevel`, {
|
2018-09-23 14:14:14 +00:00
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
|
|
|
}).then(resp => resp.json(),
|
|
|
|
err => rej(err))
|
|
|
|
.then(data => {
|
|
|
|
if (data.error === "0") {
|
|
|
|
res(data.data);
|
|
|
|
} else {
|
|
|
|
rej(data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2018-09-06 18:05:21 +00:00
|
|
|
}
|
|
|
|
|
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-23 20:22:22 +00:00
|
|
|
fetch(`${BACKEND_URL}/api/level/${id}/vocab`, {
|
2018-09-20 19:03:46 +00:00
|
|
|
method: "GET",
|
|
|
|
headers: new Headers({
|
|
|
|
"Content-Type": "application/json",
|
|
|
|
"Token": this.props.user.sessionToken,
|
|
|
|
}),
|
2018-09-23 14:14:14 +00:00
|
|
|
}).then(data => data.json(), err => {
|
|
|
|
rej(err);
|
|
|
|
}).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-23 20:22:22 +00:00
|
|
|
fetch(`${BACKEND_URL}/api/login`, {
|
2018-09-16 15:16:24 +00:00
|
|
|
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
|
|
|
}),
|
2018-09-23 14:14:14 +00:00
|
|
|
}).then(data => data.json(), err => {
|
|
|
|
// The fetch failed
|
|
|
|
rej(err);
|
|
|
|
}).then((resp: IResponse) => {
|
|
|
|
if (resp.error === "0") {
|
|
|
|
// Successful login
|
|
|
|
this.props.setUser(resp.data);
|
2018-09-23 14:30:14 +00:00
|
|
|
this.props.setDidLogin(true);
|
2018-09-23 14:14:14 +00:00
|
|
|
setSessionToken(window, resp.data.sessionToken);
|
|
|
|
this.props.setAuthenticated(true);
|
2018-09-19 16:18:32 +00:00
|
|
|
|
2018-09-23 14:14:14 +00:00
|
|
|
res(resp.data);
|
|
|
|
} else {
|
|
|
|
rej(resp);
|
|
|
|
}
|
|
|
|
});
|
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-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
|
2018-09-23 14:14:14 +00:00
|
|
|
getNextLevel={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
|
|
|
}
|
|
|
|
};
|