refactor: Simplify API calls
API calls can now make with a simple wrapper function. Additionally, the error code "200" now means success for all API calls.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "seminarfach",
|
||||
"version": "1.2.0",
|
||||
"version": "1.3.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
|
||||
33
frontend/src/api/call.ts
Normal file
33
frontend/src/api/call.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { BACKEND_URL } from "../config.in";
|
||||
|
||||
interface IAPIOptions {
|
||||
token?: string;
|
||||
body?: any;
|
||||
method: "post" | "get";
|
||||
};
|
||||
export function makeAPICall<T>(endpoint: string, options: IAPIOptions): Promise<T> {
|
||||
const { token, body, method } = options;
|
||||
const headers = token !== "" ? ({
|
||||
"Content-Type": "application/json",
|
||||
"Token": token,
|
||||
}) : ({
|
||||
"Content-Type": "application/json",
|
||||
});
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}${endpoint}`, {
|
||||
// WHUT
|
||||
headers: new Headers(headers),
|
||||
body: body !== {} ? JSON.stringify(body) : "",
|
||||
method,
|
||||
})
|
||||
.then(resp => resp.json(), err => rej(err))
|
||||
.then(data => {
|
||||
if (data.error === "200") {
|
||||
res(data.data);
|
||||
} else {
|
||||
rej(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -17,6 +17,7 @@ import VocabPage from "../containers/VocabPage";
|
||||
import Drawer from "../containers/Drawer";
|
||||
|
||||
import { trackAction } from "../api/tracker";
|
||||
import { makeAPICall } from "../api/call";
|
||||
|
||||
import { BACKEND_URL } from "../config.in";
|
||||
|
||||
@@ -60,76 +61,26 @@ export default class Application extends React.Component<IProps> {
|
||||
// Track the end of a review
|
||||
trackAction(TrackerEvent.LOG_IN);
|
||||
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/user/me`, {
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Token": token,
|
||||
}),
|
||||
}).then(resp => resp.json(), err => rej(err))
|
||||
.then(data => {
|
||||
if (data.error === "0") {
|
||||
res(data.data);
|
||||
} else {
|
||||
rej(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
return makeAPICall("/api/user/me", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
})
|
||||
}
|
||||
|
||||
getVocab = (): Promise<IVocab[]> => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getVocab = () => makeAPICall("/api/user/vocab", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
})
|
||||
|
||||
getLevels = (): Promise<ILevel[]> => {
|
||||
return new Promise((res, rej) => {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getLevels = () => makeAPICall("/api/levels", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getLastReview = () => makeAPICall("/api/user/lastReview", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
// TODO: Type?
|
||||
setLastReview = (meta: IReviewMetadata, sm2: any, delta: number) => {
|
||||
@@ -138,149 +89,55 @@ export default class Application extends React.Component<IProps> {
|
||||
this.props.setUserScoreDelta(delta);
|
||||
|
||||
// Tell the server about the last review
|
||||
fetch(`${BACKEND_URL}/api/user/lastReview`, {
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Token": this.props.user.sessionToken,
|
||||
}),
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
makeAPICall("/api/user/lastReview", {
|
||||
token: this.props.user.sessionToken,
|
||||
body: {
|
||||
meta,
|
||||
sm2,
|
||||
delta,
|
||||
}),
|
||||
}).then(resp => resp.json(), err => {
|
||||
console.log("Application::setLastReview: POSTing last results failed");
|
||||
},
|
||||
method: "post",
|
||||
});
|
||||
|
||||
// Track the end of a review
|
||||
trackAction(TrackerEvent.FINISH_LEARNING);
|
||||
}
|
||||
|
||||
getReviewQueue = (): Promise<IVocab[]> => {
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/user/queue`, {
|
||||
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.queue);
|
||||
} else {
|
||||
rej(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getReviewQueue = () => makeAPICall("/api/user/queue", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
getTopTenLearners = (): Promise<TopTen[]> => {
|
||||
// TODO: Deprecate?
|
||||
const id = this.props.user.classId;
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/class/${id}/topTen`, {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getNextLevel = () => makeAPICall("/api/user/nextLevel", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
getNextLevel = (): Promise<ILevel> => {
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/user/nextLevel`, {
|
||||
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);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
getLevelVocab = (id: number) => makeAPICall(`/api/level/${id}/vocab`, {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
getLevelVocab = (id: number): Promise<IVocab[]> => {
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/level/${id}/vocab`, {
|
||||
method: "GET",
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Token": this.props.user.sessionToken,
|
||||
}),
|
||||
}).then(data => data.json(), err => {
|
||||
rej(err);
|
||||
}).then((resp: IResponse) => {
|
||||
if (resp.error === "0") {
|
||||
res(resp.data.vocab);
|
||||
} else {
|
||||
rej(resp);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
// NOTE: This is not a promise, as we do not care about any response
|
||||
// being sent, since we don't need to update any client-side
|
||||
// state.
|
||||
introDontShowAgain = () => makeAPICall("/api/user/showWelcome", {
|
||||
token: this.props.user.sessionToken,
|
||||
body: {
|
||||
state: false,
|
||||
},
|
||||
method: "post",
|
||||
});
|
||||
|
||||
introDontShowAgain = (): void => {
|
||||
// NOTE: This is not a promise, as we do not care about any response
|
||||
// being sent, since we don't need to update any client-side
|
||||
// state.
|
||||
fetch(`${BACKEND_URL}/api/user/showWelcome`, {
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Token": this.props.user.sessionToken,
|
||||
}),
|
||||
method: "POST",
|
||||
body: JSON.stringify({
|
||||
state: false,
|
||||
}),
|
||||
});
|
||||
}
|
||||
getDashboard = () => makeAPICall("/api/user/dashboard", {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "get",
|
||||
});
|
||||
|
||||
// TODO: Type?
|
||||
getDashboard = (): Promise<any> => {
|
||||
return new Promise((res, rej) => {
|
||||
fetch(`${BACKEND_URL}/api/user/dashboard`, {
|
||||
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 {
|
||||
console.log("Application::getDashboard: Failed to get dashboard");
|
||||
rej(data);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
updateDoneLevels = (id: string): void => {
|
||||
fetch(`${BACKEND_URL}/api/user/level/${id}`, {
|
||||
headers: new Headers({
|
||||
"Content-Type": "application/json",
|
||||
"Token": this.props.user.sessionToken,
|
||||
}),
|
||||
method: "POST",
|
||||
});
|
||||
}
|
||||
updateDoneLevels = (id: string) => makeAPICall(`/api/user/level/${id}`, {
|
||||
token: this.props.user.sessionToken,
|
||||
method: "post",
|
||||
});
|
||||
|
||||
login = (username: string, password: string): Promise<IUser | IResponse> => {
|
||||
// Track the login
|
||||
@@ -302,7 +159,7 @@ export default class Application extends React.Component<IProps> {
|
||||
// The fetch failed
|
||||
rej(err);
|
||||
}).then((resp: IResponse) => {
|
||||
if (resp.error === "0") {
|
||||
if (resp.error === "200") {
|
||||
// Successful login
|
||||
this.props.setUser(resp.data);
|
||||
this.props.setDidLogin(true);
|
||||
|
||||
@@ -28,7 +28,7 @@ import { IVocab, VocabType } from "../models/vocab";
|
||||
|
||||
interface IProps {
|
||||
id: string;
|
||||
levelVocab: (id: string) => Promise<IVocab[]>;
|
||||
levelVocab: (id: string) => Promise<any>;
|
||||
|
||||
history: any;
|
||||
|
||||
@@ -64,7 +64,8 @@ const LevelPageWithRouter = withRouter(
|
||||
this.props.setLoading(true);
|
||||
|
||||
// TODO: Error handling
|
||||
this.props.levelVocab(this.props.id).then(vocab => {
|
||||
this.props.levelVocab(this.props.id).then(data => {
|
||||
const { vocab } = data;
|
||||
this.props.setVocab(vocab);
|
||||
this.props.setCurrentVocab(vocab[0]);
|
||||
this.props.setLookedAt([vocab[0].id]);
|
||||
|
||||
@@ -6,7 +6,6 @@ import Button from "@material-ui/core/Button";
|
||||
import Card from '@material-ui/core/Card';
|
||||
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 Loader from "../components/loading";
|
||||
@@ -17,7 +16,7 @@ import { ILevel } from "../models/level";
|
||||
import { IUser } from "../models/user";
|
||||
|
||||
interface IProps {
|
||||
getLevels: () => Promise<ILevel[]>;
|
||||
getLevels: () => Promise<any>;
|
||||
|
||||
history: any;
|
||||
|
||||
@@ -37,7 +36,7 @@ const LevelListWithRouter = withRouter(
|
||||
|
||||
// Fetch the levels
|
||||
this.props.getLevels().then(res => {
|
||||
this.props.setLevels(res);
|
||||
this.props.setLevels(res.levels);
|
||||
this.props.setLoading(false);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ import { Queue } from "../utils/queue";
|
||||
|
||||
interface IProps {
|
||||
levelId?: number;
|
||||
vocabByLevel?: (level: number) => Promise<IVocab[]>;
|
||||
vocabByQueue?: () => Promise<IVocab[]>;
|
||||
vocabByLevel?: (level: number) => Promise<any>;
|
||||
vocabByQueue?: () => Promise<any>;
|
||||
updateDoneLevels?: (id: string) => void;
|
||||
setLastReview: (meta: IReviewMetadata, sm2: any, delta: number) => void;
|
||||
reviewType: ReviewType;
|
||||
@@ -110,19 +110,19 @@ const ReviewPageWithRouter = withRouter(
|
||||
// Track the start of a session
|
||||
trackAction(TrackerEvent.START_LEARNING);
|
||||
|
||||
getVocab().then((res: IVocab[]) => {
|
||||
getVocab().then((res: any) => {
|
||||
// Check if we received any vocabulary
|
||||
if (res.length === 0) {
|
||||
if (res.vocab.length === 0) {
|
||||
this.openModal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Stop the loading
|
||||
this.props.setLoading(false);
|
||||
this.vocab = res;
|
||||
this.vocab = res.vocab;
|
||||
|
||||
// Convert the vocab items into review queue cards
|
||||
res.forEach(vocab => {
|
||||
res.vocab.forEach((vocab: IVocab) => {
|
||||
// Set the error data for the group
|
||||
this.error_data[vocab.id] = 0;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user