Skip to content

Commit

Permalink
Merge branch 'development'
Browse files Browse the repository at this point in the history
  • Loading branch information
TeoTN committed Dec 28, 2017
2 parents f19e981 + 77f885d commit 062477e
Show file tree
Hide file tree
Showing 66 changed files with 4,718 additions and 4,466 deletions.
423 changes: 215 additions & 208 deletions config/webpack.config.dev.js

Large diffs are not rendered by default.

497 changes: 252 additions & 245 deletions config/webpack.config.prod.js

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "tfoosball",
"version": "2.2.1",
"version": "2.3.0",
"private": true,
"devDependencies": {
"autoprefixer": "6.5.1",
Expand Down Expand Up @@ -50,6 +50,7 @@
"path-exists": "2.1.0",
"postcss-loader": "1.0.0",
"promise": "7.1.1",
"raw-loader": "0.5.1",
"react-addons-test-utils": "15.4.2",
"react-dev-utils": "0.4.2",
"recursive-readdir": "2.1.0",
Expand All @@ -65,21 +66,25 @@
"babel-polyfill": "6.20.0",
"chart.js": "2.5.0",
"es6-error": "4.0.2",
"lodash": "4.17.4",
"md5": "2.2.1",
"normalizr": "3.2.4",
"promise-window": "1.1.0",
"randy": "1.5.1",
"react": "15.4.2",
"react-bootstrap": "0.30.7",
"react-dom": "15.4.2",
"react-fontawesome": "1.5.0",
"react-markdown": "3.1.3",
"react-redux": "5.0.3",
"react-router": "3.0.2",
"react-router-bootstrap": "0.23.1",
"react-select": "1.0.0-rc.5",
"redux": "3.6.0",
"redux-form": "6.6.1",
"redux-saga": "0.14.3",
"redux-saga-router": "2.1.0"
"redux-saga-router": "2.1.0",
"reselect": "3.0.1"
},
"scripts": {
"heroku-prebuild": "cd tfoostrap && npm i && npm run build && npm link && cd .. && npm link @tfoosball/tfoostrap",
Expand Down
67 changes: 36 additions & 31 deletions src/api/helpers.js
Original file line number Diff line number Diff line change
@@ -1,31 +1,36 @@
import {APIError, APIUnauthorizedError, APIForbiddenError} from '../errors';

export const retryOnError = (fn, errClass=Error) => fn().catch(error => {
if (error.constructor === errClass) {
return fn();
}
return Promise.reject(error);
});

const ignoreSyntax = (error) => error.constructor.name === 'SyntaxError' ? '' : error;
export const ensureJSON = (response) => response.json().catch(ignoreSyntax);

export const ensureSuccessOr = (errorMsg) =>
(response) => {

if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response);
} else if (response.status === 403) {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new APIForbiddenError(errorMsg, responseBody)));
} else if (response.status === 401) {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new APIUnauthorizedError(responseBody)));
} else {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new APIError(errorMsg, responseBody)));
}
};
import * as fromErrors from '../errors';

export const retryOnError = (fn, errClass=Error) => fn().catch(error => {
if (error.constructor === errClass) {
return fn();
}
return Promise.reject(error);
});

const ignoreSyntax = (error) => error.constructor.name === 'SyntaxError' ? '' : error;
export const ensureJSON = (response) => response.json().catch(ignoreSyntax);

export const ensureSuccessOr = (errorMsg) =>
(response) => {

if (response.status >= 200 && response.status < 300) {
return Promise.resolve(response);
} else if (response.status === 403) {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new fromErrors.APIForbiddenError(errorMsg, responseBody)));
} else if (response.status === 401) {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new fromErrors.APIUnauthorizedError(responseBody)));
} else if (response.status === 400) {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new fromErrors.APIValidationError(responseBody)))
}
else {
return response.json()
.catch(ignoreSyntax)
.then(responseBody => Promise.reject(new fromErrors.APIError(responseBody)));
}
};
162 changes: 81 additions & 81 deletions src/api/index.js
Original file line number Diff line number Diff line change
@@ -1,81 +1,81 @@
import { API_ROOT } from './config';
import { loadState } from '../persistence';
import { ensureJSON, ensureSuccessOr } from './helpers';

const getDefaultHeaders = () => {
const persistedState = loadState();
const headers = new Headers();
headers.append('Content-Type', 'application/json');
if (persistedState && persistedState.hasOwnProperty('auth')) {
headers.append('Authorization', `Token ${persistedState.auth.token}`);
}
return headers;
};

const api = {
requests: {
get(url, params, error_msg='Failed to retrieve data from server') {
let target = url;
if (params && Object.keys(params).length > 0) {
const queryParams = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&');
target = `${url}?${queryParams}`;
}
const request = new Request(target, {
method: 'GET',
headers: getDefaultHeaders(),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
post(url, body, error_msg='Failed to send data to server') {
const request = new Request(url, {
method: 'POST',
headers: getDefaultHeaders(),
body: JSON.stringify(body),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
patch(url, body, error_msg='Failed to send data to server') {
const request = new Request(url, {
method: 'PATCH',
headers: getDefaultHeaders(),
body: JSON.stringify(body),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
['delete'](url, error_msg='Failed to delete data on server') { // eslint-disable-line no-useless-computed-key
const request = new Request(url, {
method: 'DELETE',
headers: getDefaultHeaders(),
});
return fetch(request)
.then(ensureSuccessOr(error_msg));
},
},
urls: {
playerList: () => `${API_ROOT}/players/`,
playerEntity: (player_id) => `${API_ROOT}/players/${player_id}/`,
playerEntityInvite: (player_id) => `${API_ROOT}/players/${player_id}/invite/`,
teamList: () => `${API_ROOT}/teams/`,
teamListJoined: () => `${API_ROOT}/teams/joined/`,
teamJoin: () => `${API_ROOT}/teams/join/`,
teamEntity: (team_id) => `${API_ROOT}/teams/${team_id}/`,
teamAccept: () => `${API_ROOT}/teams/accept/`,
teamInvite: (team_id) => `${API_ROOT}/teams/${team_id}/invite/`,
teamMemberList: (team_id) => `${API_ROOT}/teams/${team_id}/members/`,
teamMemberEntity: (team_id, member_id) => `${API_ROOT}/teams/${team_id}/members/${member_id}/`,
teamMatchList: (team_id) => `${API_ROOT}/teams/${team_id}/matches/`,
teamMatchEntity: (team_id, match_id) => `${API_ROOT}/teams/${team_id}/matches/${match_id}/`,
teamMatchPoints: (team_id) => `${API_ROOT}/teams/${team_id}/matches/points/`,
profile: () => `${API_ROOT}/rest-auth/user/`,
logout: () => `${API_ROOT}/rest-auth/logout/`
}
};


export default api;
import { API_ROOT } from './config';
import { loadState } from '../persistence';
import { ensureJSON, ensureSuccessOr } from './helpers';

const getDefaultHeaders = () => {
const persistedState = loadState();
const headers = new Headers();
headers.append('Content-Type', 'application/json');
if (persistedState && persistedState.hasOwnProperty('auth')) {
headers.append('Authorization', `Token ${persistedState.auth.token}`);
}
return headers;
};

const api = {
requests: {
get(url, params, error_msg='Failed to retrieve data from server') {
let target = url;
if (params && Object.keys(params).length > 0) {
const queryParams = Object.entries(params).map(([key, value]) => `${key}=${value}`).join('&');
target = `${url}?${queryParams}`;
}
const request = new Request(target, {
method: 'GET',
headers: getDefaultHeaders(),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
post(url, body, error_msg='Failed to send data to server') {
const request = new Request(url, {
method: 'POST',
headers: getDefaultHeaders(),
body: JSON.stringify(body),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
patch(url, body, error_msg='Failed to send data to server') {
const request = new Request(url, {
method: 'PATCH',
headers: getDefaultHeaders(),
body: JSON.stringify(body),
});
return fetch(request)
.then(ensureSuccessOr(error_msg))
.then(ensureJSON);
},
['delete'](url, error_msg='Failed to delete data on server') { // eslint-disable-line no-useless-computed-key
const request = new Request(url, {
method: 'DELETE',
headers: getDefaultHeaders(),
});
return fetch(request)
.then(ensureSuccessOr(error_msg));
},
},
urls: {
playerList: () => `${API_ROOT}/players/`,
playerEntity: (player_id) => `${API_ROOT}/players/${player_id}/`,
playerEntityInvite: (player_id) => `${API_ROOT}/players/${player_id}/invite/`,
teamList: () => `${API_ROOT}/teams/`,
teamListJoined: () => `${API_ROOT}/teams/joined/`,
teamJoin: () => `${API_ROOT}/teams/join/`,
teamEntity: (team_id) => `${API_ROOT}/teams/${team_id}/`,
teamAccept: () => `${API_ROOT}/teams/accept/`,
teamInvite: (team_id) => `${API_ROOT}/teams/${team_id}/invite/`,
teamMemberList: (team_id) => `${API_ROOT}/teams/${team_id}/members/`,
teamMemberEntity: (team_id, member_id) => `${API_ROOT}/teams/${team_id}/members/${member_id}/`,
teamMatchList: (team_id) => `${API_ROOT}/teams/${team_id}/matches/`,
teamMatchEntity: (team_id, match_id) => `${API_ROOT}/teams/${team_id}/matches/${match_id}/`,
teamMatchPoints: (team_id) => `${API_ROOT}/teams/${team_id}/matches/points/`,
profile: () => `${API_ROOT}/rest-auth/user/`,
logout: () => `${API_ROOT}/rest-auth/logout/`
}
};


export default api;
66 changes: 41 additions & 25 deletions src/errors.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,41 @@
import ExtendableError from 'es6-error';

export class APIError extends ExtendableError {
constructor(message, response) {
const msg = response.hasOwnProperty('shouldDisplay') && response.shouldDisplay ? response.message : message;
super(msg);
this.serverResponse = response;
}

toDebugString() {
return JSON.stringify(this.serverResponse, null, 2);
}
}

export class APIUnauthorizedError extends APIError {
constructor(serverResponse) {
super('Session has expired.', serverResponse);
}
}

export class APIForbiddenError extends APIError {
constructor(serverResponse) {
super('Access denied', serverResponse);
}
}
import ExtendableError from 'es6-error';

export class APIError extends ExtendableError {
constructor(message, response) {
const msg = response.hasOwnProperty('shouldDisplay') && response.shouldDisplay ? response.message : message;
super(msg);
this.serverResponse = response;
}

toDebugString() {
return JSON.stringify(this.serverResponse, null, 2);
}
}

export class APIUnauthorizedError extends APIError {
constructor(serverResponse) {
super('Session has expired.', serverResponse);
}
}

export class APIForbiddenError extends APIError {
constructor(serverResponse) {
super('Access denied', serverResponse);
}
}


export class APIValidationError extends ExtendableError {
constructor(serverResponse) {
if (serverResponse.hasOwnProperty('non_field_errors')) {
super(serverResponse.non_field_errors.join(', '));
} else {
super('Failed to validate data');
}
this.serverResponse = serverResponse;
}

toDebugString() {
return JSON.stringify(this.serverResponse, null, 2);
}
}
6 changes: 3 additions & 3 deletions src/homepage/components/App.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react';
import Header from '../../shared/components/Header';
import ModalMessage from '../../shared/components/QuestionModal';
import ModalMessage from '../../shared/components/ModalMessage';
import { ListGroupItem } from 'react-bootstrap';
import {utils} from 'react-bootstrap';

Expand All @@ -20,10 +20,10 @@ export default class App extends React.Component {
</main>
<div className="filler"/>
<footer className="footer-app">
Carefully crafted by <a href="http://www.piotrstaniow.pl/">Piotr Staniów</a>. [v2.2.1]
Carefully crafted by <a href="http://www.piotrstaniow.pl/">Piotr Staniów</a>. [v2.3.0]
</footer>
</div>
);
}

}
}
Loading

0 comments on commit 062477e

Please sign in to comment.